bzoj 4911: [Sdoi2017]切树游戏
考虑维护原树的lct,在上面dp,由于dp方程特殊,均为异或卷积或加法,计算中可以只使用fwt后的序列
v[w]表示联通子树的最浅点为w,且不选w的splay子树中的点
l[w]表示联通子树的最浅点在w的lct子树中,且选w的splay子树中极左点(w的splay子树为{w}+{u的splay子树,满足u==ch[w][0]||u==ch[w][1]})
r[w]表示联通子树的最浅点在w的lct子树中,且选w的splay子树中极右点
lr[w]表示联通子树的最浅点为w,且选w的splay子树中所有的点
s[w]表示联通子树的最浅点在w的lct子树中(w的lct子树为{w}+{u的lct子树,满足fa[u]==w})
ss[w]表示联通子树的最浅点在w的lct子树中,且不选w
时间复杂度O((n+q)mlogm),由于dp方程复杂以及lct自带的常数,总体常数较大
#include<bits/stdc++.h>
#define fm(i) for(int i=0;i<m;++i)
typedef int arr[];
const int N=,P=;
int n,m,v0[N],ivs[],*iv=ivs+P+;
arr x0[],ans,tmp;
std::vector<int>e[N];
struct node{
node*c[],*f;
arr v,vt,l,r,lr,s,ss;
bool nrt();
void up();
void setrc(node*w);
}ns[N],*nil=ns,*rt=ns+;
void cpy(int*a,int*b){memcpy(a,b,sizeof(int)*m);}
bool node::nrt(){return this==f->c[]||this==f->c[];}
void node::up(){
fm(i){
int vi=vt[i]?:v[i];
int vc0lr=vi*c[]->lr[i]%P;
int vc0r=vi*c[]->r[i]%P;
l[i]=(vc0lr*c[]->l[i]+c[]->l[i])%P;
r[i]=(vc0r*c[]->lr[i]+c[]->r[i])%P;
lr[i]=vc0lr*c[]->lr[i]%P;
s[i]=(vc0r*c[]->l[i]+ss[i])%P;
}
}
void mul(int*v1,int*t,int*v2){fm(i)v2[i]?v1[i]=v1[i]*v2[i]%P:++t[i];}
void div(int*v1,int*t,int*v2){fm(i)v2[i]?v1[i]=v1[i]*iv[v2[i]]%P:--t[i];}
void node::setrc(node*w){
if(c[]!=nil)mul(v,vt,c[]->l);
c[]=w;
if(c[]!=nil)div(v,vt,c[]->l);
up();
}
void rot(node*w){
node*f=w->f,*g=f->f;
int d=w==f->c[];
if(f->nrt())g->c[g->c[]==f]=w;
w->f=g;
(f->c[d]=w->c[d^])->f=f;
(w->c[d^]=f)->f=w;
fm(i){
int x=f->ss[i],y=f->c[d]->s[i];
f->ss[i]=(x-w->s[i]+y)%P;
w->s[i]=f->s[i];
w->ss[i]-=y;
}
f->up();
fm(i)w->ss[i]=(w->ss[i]+f->s[i])%P;
}
void sp(node*w){
if(!w->nrt())return;
do{
node*f=w->f;
if(f->nrt())rot((f->c[]==w)==(f->f->c[]==f)?f:w);
rot(w);
}while(w->nrt());
w->up();
}
void acs(node*x){
rt=x;
for(node*y=nil;x!=nil;sp(x),x->setrc(y),y=x,x=x->f);
sp(rt);
}
void dfs(int w,int pa){
for(int i=;i<e[w].size();++i){
int u=e[w][i];
if(u!=pa){
dfs(u,w);
ns[u].f=ns+w;
mul(ns[w].v,ns[w].vt,ns[u].l);
fm(j)ns[w].ss[j]+=ns[u].s[j];
}
}
ns[w].up();
}
void dwt(int*a){
for(int i=;i<m;i<<=){
for(int j=;j<m;j+=i<<){
int*b=a+j,*c=b+i;
for(int k=;k<i;++k){
int x=b[k],y=c[k];
b[k]=x+y;
c[k]=x-y;
}
}
}
fm(i)a[i]%=P;
}
char buf[],*ptr=buf;
int _(){
int x=;
while(*ptr<)++ptr;
while(*ptr>)x=x*+*ptr++-;
return x;
}
int main(){
fread(buf,,sizeof(buf),stdin);
n=_();m=_();
for(int i=;i<m;++i){
x0[i][i]=;
dwt(x0[i]);
}
iv[-P]=iv[]=;
for(int i=;i<P;++i)iv[i-P]=iv[i]=(P-P/i)*iv[P%i]%P;
for(int i=;i<=n;++i)ns[i]=(node){nil,nil,nil};
cpy(nil->l,x0[]);
cpy(nil->r,x0[]);
cpy(nil->lr,x0[]);
for(int i=;i<=n;++i){
v0[i]=_();
cpy(ns[i].v,x0[v0[i]]);
}
for(int i=,a,b;i<n;++i){
a=_(),b=_();
e[a].push_back(b);
e[b].push_back(a);
}
dfs(,);
for(int q=_(),ed=;q;--q){
if(_()==){
if(ed){
cpy(ans,rt->s);
dwt(ans);
ed=;
}
printf("%d\n",(ans[_()]*iv[m]%P+P)%P);
}else{
ed=;
int w=_(),x=_();
acs(ns+w);
int*v1=x0[v0[w]],*v2=x0[x];
fm(i)rt->v[i]=rt->v[i]*v1[i][iv]%P*v2[i]%P;
fm(i)ans[i]=rt->vt[i]?:rt->v[i];
dwt(ans);
v0[w]=x;
rt->up();
}
}
return ;
}
由于树形态不改变,也可以建一棵深度不超过logn+O(1)的静态lct,可以明显减小常数
#include<bits/stdc++.h>
#define fm(i) for(int i=0;i<m;++i)
typedef int arr[];
const int N=,P=;
int n,m,v0[N],ivs[],*iv=ivs+P+;
arr x0[],ans,tmp;
std::vector<int>e[N];
struct node{
node*c[],*f;
arr v,vt,l,r,lr,s,ss;
bool isrt();
void up();
}ns[N],*nil=ns,*rt;
bool node::isrt(){return this!=f->c[]&&this!=f->c[];}
void cpy(int*a,int*b){memcpy(a,b,sizeof(int)*m);}
void node::up(){
fm(i){
int vi=vt[i]?:v[i];
int vc0lr=vi*c[]->lr[i]%P;
int vc0r=vi*c[]->r[i]%P;
l[i]=(vc0lr*c[]->l[i]+c[]->l[i])%P;
r[i]=(vc0r*c[]->lr[i]+c[]->r[i])%P;
lr[i]=vc0lr*c[]->lr[i]%P;
s[i]=(vc0r*c[]->l[i]+ss[i])%P;
}
}
void mul(int*v1,int*t,int*v2){fm(i)v2[i]?v1[i]=v1[i]*v2[i]%P:++t[i];}
void div(int*v1,int*t,int*v2){fm(i)v2[i]?v1[i]=v1[i]*iv[v2[i]]%P:--t[i];}
int fa[N],sz[N],son[N],dep[N],top[N],ws[N],wp;
void f1(int w,int pa){
dep[w]=dep[fa[w]=pa]+(sz[w]=);
for(int i=;i<e[w].size();++i){
int u=e[w][i];
if(u!=pa){
f1(u,w);
sz[w]+=sz[u];
if(sz[u]>sz[son[w]])son[w]=u;
}
}
ns[w].up();
}
node*build(int L,int R,node*f){
if(L>R)return nil;
int L0=L,R0=R;
for(int M,s0=ws[L][sz]+ws[R+][sz];L<R;ws[M=L+R+>>][sz]*<s0?R=M-:L=M);
node*w=ns+ws[L];
w->c[]=build(L0,L-,w);
w->c[]=build(L+,R0,w);
w->f=f;
w->up();
if(f!=nil){
fm(i)f->ss[i]+=w->s[i];
if(L0==&&R0==wp)mul(f->v,f->vt,w->l);
}
return w;
}
void f2(int w,int tp){
top[w]=tp;
for(int i=;i<e[w].size();++i){
int u=e[w][i];
if(u!=fa[w]&&u!=son[w])f2(u,u);
}
if(son[w])f2(son[w],tp);
else{
wp=;
for(int a=tp;a;a=son[a])ws[++wp]=a;
ws[wp+]=;
rt=build(,wp,ns+fa[tp]);
}
}
void dwt(int*a){
for(int i=;i<m;i<<=){
for(int j=;j<m;j+=i<<){
int*b=a+j,*c=b+i;
for(int k=;k<i;++k){
int x=b[k],y=c[k];
b[k]=x+y;
c[k]=x-y;
}
}
}
fm(i)a[i]%=P;
}
char buf[],*ptr=buf;
int _(){
int x=;
while(*ptr<)++ptr;
while(*ptr>)x=x*+*ptr++-;
return x;
}
int main(){
fread(buf,,sizeof(buf),stdin);
n=_();m=_();
for(int i=;i<m;++i){
x0[i][i]=;
dwt(x0[i]);
}
iv[-P]=iv[]=;
for(int i=;i<P;++i)iv[i-P]=iv[i]=(P-P/i)*iv[P%i]%P;
for(int i=;i<=n;++i)ns[i]=(node){nil,nil,nil};
cpy(nil->l,x0[]);
cpy(nil->r,x0[]);
cpy(nil->lr,x0[]);
for(int i=;i<=n;++i){
v0[i]=_();
cpy(ns[i].v,x0[v0[i]]);
}
for(int i=,a,b;i<n;++i){
a=_(),b=_();
e[a].push_back(b);
e[b].push_back(a);
}
f1(,);f2(,);
for(int q=_(),ed=;q;--q){
if(_()==){
if(ed){
cpy(ans,rt->s);
dwt(ans);
ed=;
}
printf("%d\n",(ans[_()]*iv[m]%P+P)%P);
}else{
ed=;
int w=_(),x=_(),stp=;
node*_w=ns+w,*stk[];
for(node*a=_w;a!=nil;stk[++stp]=a,a=a->f);
for(int i=stp;i>;--i){
int*v1=stk[i]->ss,*v2=stk[i-]->s;
fm(j)v1[j]-=v2[j];
if(stk[i-]->isrt())div(stk[i]->v,stk[i]->vt,stk[i-]->l);
}
int*v1=x0[v0[w]],*v2=x0[x];
fm(i)_w->v[i]=_w->v[i]*v1[i][iv]%P*v2[i]%P;
_w->up();
for(int i=;i<=stp;++i){
int*v1=stk[i]->ss,*v2=stk[i-]->s;
fm(j)v1[j]=(v1[j]+v2[j])%P;
if(stk[i-]->isrt())mul(stk[i]->v,stk[i]->vt,stk[i-]->l);
stk[i]->up();
}
v0[w]=x;
}
}
return ;
}
bzoj 4911: [Sdoi2017]切树游戏的更多相关文章
- 【BZOJ4911】[SDOI2017]切树游戏(动态dp,FWT)
[BZOJ4911][SDOI2017]切树游戏(动态dp,FWT) 题面 BZOJ 洛谷 LOJ 题解 首先考虑如何暴力\(dp\),设\(f[i][S]\)表示当前以\(i\)节点为根节点,联通子 ...
- BZOJ4911: [Sdoi2017]切树游戏
BZOJ 4911 切树游戏 重构了三次.jpg 每次都把这个问题想简单了.jpg 果然我还是太菜了.jpg 这种题的题解可以一眼秒掉了,FWT+动态DP简直是裸的一批... 那么接下来,考虑如何维护 ...
- LG3781 [SDOI2017]切树游戏
题意 题目描述 小Q是一个热爱学习的人,他经常去维基百科学习计算机科学. 就在刚才,小Q认真地学习了一系列位运算符,其中按位异或的运算符\(\oplus\)对他影响很大.按位异或的运算符是双目运算符. ...
- LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】
题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...
- [SDOI2017]切树游戏
题目 二轮毒瘤题啊 辣鸡洛谷竟然有卡树剖的数据 还是\(loj\)可爱 首先这道题没有带修,设\(dp_{i,j}\)表示以\(i\)为最高点的连通块有多少个异或和为\(j\),\(g_{i,j}=\ ...
- 洛谷 P3781 - [SDOI2017]切树游戏(动态 DP+FWT)
洛谷题面传送门 SDOI 2017 R2 D1 T3,nb tea %%% 讲个笑话,最近我在学动态 dp,wjz 在学 FWT,而我们刚好在同一天做到了这道题,而这道题刚好又是 FWT+动态 dp ...
- 【LOJ】#2269. 「SDOI2017」切树游戏
题解 把所有的数组一开始就FWT好然后再IFWT回去可以减小常数 从13s跑到0.7s-- 可以参照immortalCO的论文,感受一下毒瘤的动态动态DP 就是用数据结构维护线性递推的矩阵的乘积 由于 ...
- loj#2269. 「SDOI2017」切树游戏
还是loj的机子快啊... 普通的DP不难想到,设F[i][zt]为带上根玩出zt的方案数,G[i][zt]为子树中的方案数,后面是可以用FWT优化的 主要是复习了下动态DP #include< ...
- LOJ2269. 「SDOI2017」切树游戏 [FWT,动态DP]
LOJ 思路 显然是要DP的.设\(dp_{u,i}\)表示\(u\)子树内一个包含\(u\)的连通块异或出\(i\)的方案数,发现转移可以用FWT优化,写成生成函数就是这样的: \[ dp_{u}= ...
随机推荐
- SharePoint 2013的100个新功能之网站管理(一)
一:设置盘 网站操作现在被替换为新的(设置)盘子.一些新的操作像添加一个应用.添加一个页面或设计管理器被添加而像创建网站则从菜单中移除了. 二:移除以其他用户身份登录 在SharePoint 2013 ...
- VM VirtualBox虚拟机与物理主机之间的复制
物理主机: 系统:Ubuntu 11.04 X86_64 虚拟机: 系统:Windows XP Pack3 点击虚拟机的 设备->安装增强功能即可 安装后两系统之间的复制,粘贴可正常使用,如同一 ...
- Java性能优化之JVM GC(垃圾回收机制)
Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.st ...
- Linux下C++的通用Makefile与解析
本文给出万能Makefile的具体实现,以及对其中的关键点进行解析.所谓C++万能Makefile,即可编译链接所有的C++程序,而只需作很少的修改. 号称万能Makefile,一统江湖.我对原版的M ...
- 常用增强学习实验环境 I (MuJoCo, OpenAI Gym, rllab, DeepMind Lab, TORCS, PySC2) (转载)
原文地址:http://blog.csdn.net/jinzhuojun/article/details/77144590 和其它的机器学习方向一样,强化学习(Reinforcement Learni ...
- ubuntu journalctl — 检索 systemd 日志
常用: 查看最近1000行log sudo journalctl -f --lines=1000 -u server.$PROJECT_NAME --no-full, --full, -l 如果字段内 ...
- zookeeper 入门指导
zookeeper数据模型 zookeeper有一个层级命名空间,和一个分布式文件系统非常相似 .唯一的不同是每个节点可以有关联的数据,子节点也是.就像有一个文件系统,并且允许文件可以是一个目录. ...
- HPU 1471:又是斐波那契数列??(大数取模)
1471: 又是斐波那契数列?? 时间限制: 1 Sec 内存限制: 128 MB 提交: 278 解决: 27 统计 题目描述 大家都知道斐波那契数列吧?斐波那契数列的定义是这样的: f0 = 0; ...
- linux学习——sed工具
命令格式: sed [-nefr] [动作] 1.sed可以分析标准输入(STDIN)的数据,然后将数据处理后,再将他输出到标准输出(STDOUT),他有替换.删除.新增.选定特定行等处理功能.sed ...
- Heap Operations 优先队列
Petya has recently learned data structure named "Binary heap". The heap he is now operatin ...