考虑维护原树的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]切树游戏的更多相关文章

  1. 【BZOJ4911】[SDOI2017]切树游戏(动态dp,FWT)

    [BZOJ4911][SDOI2017]切树游戏(动态dp,FWT) 题面 BZOJ 洛谷 LOJ 题解 首先考虑如何暴力\(dp\),设\(f[i][S]\)表示当前以\(i\)节点为根节点,联通子 ...

  2. BZOJ4911: [Sdoi2017]切树游戏

    BZOJ 4911 切树游戏 重构了三次.jpg 每次都把这个问题想简单了.jpg 果然我还是太菜了.jpg 这种题的题解可以一眼秒掉了,FWT+动态DP简直是裸的一批... 那么接下来,考虑如何维护 ...

  3. LG3781 [SDOI2017]切树游戏

    题意 题目描述 小Q是一个热爱学习的人,他经常去维基百科学习计算机科学. 就在刚才,小Q认真地学习了一系列位运算符,其中按位异或的运算符\(\oplus\)对他影响很大.按位异或的运算符是双目运算符. ...

  4. LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】

    题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...

  5. [SDOI2017]切树游戏

    题目 二轮毒瘤题啊 辣鸡洛谷竟然有卡树剖的数据 还是\(loj\)可爱 首先这道题没有带修,设\(dp_{i,j}\)表示以\(i\)为最高点的连通块有多少个异或和为\(j\),\(g_{i,j}=\ ...

  6. 洛谷 P3781 - [SDOI2017]切树游戏(动态 DP+FWT)

    洛谷题面传送门 SDOI 2017 R2 D1 T3,nb tea %%% 讲个笑话,最近我在学动态 dp,wjz 在学 FWT,而我们刚好在同一天做到了这道题,而这道题刚好又是 FWT+动态 dp ...

  7. 【LOJ】#2269. 「SDOI2017」切树游戏

    题解 把所有的数组一开始就FWT好然后再IFWT回去可以减小常数 从13s跑到0.7s-- 可以参照immortalCO的论文,感受一下毒瘤的动态动态DP 就是用数据结构维护线性递推的矩阵的乘积 由于 ...

  8. loj#2269. 「SDOI2017」切树游戏

    还是loj的机子快啊... 普通的DP不难想到,设F[i][zt]为带上根玩出zt的方案数,G[i][zt]为子树中的方案数,后面是可以用FWT优化的 主要是复习了下动态DP #include< ...

  9. LOJ2269. 「SDOI2017」切树游戏 [FWT,动态DP]

    LOJ 思路 显然是要DP的.设\(dp_{u,i}\)表示\(u\)子树内一个包含\(u\)的连通块异或出\(i\)的方案数,发现转移可以用FWT优化,写成生成函数就是这样的: \[ dp_{u}= ...

随机推荐

  1. bzoj1082

    题解: 暴搜+二分+剪枝 二分答案,暴力判断是否有解 然后加上剪枝 代码: #include<bits/stdc++.h> using namespace std; ; int rest, ...

  2. 《Python》 文件操作

    一.文件操作基本流程: 1.文件基本操作初识: 打开文件: 文件句柄 = open(‘文件路径’,‘编码方式’,‘打开方式’) 第一种:f = open('d:\'a.txt',encoding='u ...

  3. html内容滚动

    <marquee srolldelay="50" direction="up"></marquee> 滚动标签<marquee&g ...

  4. BitDefender(比特梵德)特惠活动 免费获取9个月激活码

    Bitdefender为了庆祝自己获得2014年最佳杀毒软件,送出9个月的Internet security免费激活码.

  5. Java Socket 实现HTTP服务器核心

    原文链接:http://www.ihuxu.com/p/235.html   首先了解下HTTP协议: wikiPedia的说明很好,在此不重复了.链接:http://zh.wikipedia.org ...

  6. [转]TFS.VisualStudio.com TF30063: You are not authorized to access Collection

    If you are trying to connect to team foundation server online through visual studio and you get unau ...

  7. zabbix监控系统的配置

    一.zabbix介绍 zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以让系 ...

  8. 转:AMD规范与CMD规范的区别是什么?

    AMD规范与CMD规范的区别是什么?    在比较之前,我们得先来了解下什么是AMD规范?什么是CMD规范?当然先申明一下,我个人也是总结下而已,也是网上看到的资料,自己总结下或者可以说整理下而已,供 ...

  9. 动态更新 HTML 内容 —— AJAX

    通过 JavaScript 加载数据,在不刷新网页的情况下,更新网页内容的技术,称为 AJAX(Asynchronous JavaScript and XML,异步 JavaScript 和 XML) ...

  10. chapter02 回归模型在''美国波士顿房价预测''问题中实践

    #coding=utf8 # 从sklearn.datasets导入波士顿房价数据读取器. from sklearn.datasets import load_boston # 从sklearn.mo ...