考虑维护原树的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. 第n+1次考试

    题目: 1. 中位数 [问题描述] 给定C个不同物品,每个物品有一重量和体积,保证每个物品的重量不一样.从中选出N个物品,在体积不超过F的情况下,使得选出的物品的重量的中位数最大.所谓中位数,就是排序 ...

  2. C++静态成员变量和静态成员函数

    数据成员可以分静态变量.非静态变量两种. 静态成员:静态类中的成员加入static修饰符,即是静态成员.可以直接使用类名+静态成员名访问此静态成员,因为静态成员存在于内存,非静态成员需要实例化才会分配 ...

  3. 2.2 C++类的成员变量和成员函数

    参考:http://www.weixueyuan.net/view/6334.html 总结: 类成员的声明和定义: 类成员函数的定义分类内定义(内联)和类外定义(可用 inline 关键字 强制转换 ...

  4. iOS调用第三方地图App进行导航方法

    前言 App内根据手机上装载的地图App将其显示在弹出的选择框,选择对应地图跳转进入地图导航.需要用到- (BOOL)canOpenURL:(NSURL *)url NS_AVAILABLE_IOS( ...

  5. redis下载安装

    在linux下下载redis $ wget http://download.redis.io/releases/redis-4.0.9.tar.gz 如果没有网络的话可以先下载安装包再移到linux虚 ...

  6. Linux/Mac OS 个人常用Terminal技巧整理

    刚开始接触linux有些不适应,走了不少弯路,一直没有系统的学过linux应用,基本都是零零散散Google出来的知识,在这里做个整理: Vi/Vim 基本操作: 刚开始接触linux时,不懂vi吃了 ...

  7. SWIFT中将信息保存到plist文件内

    在项目中可能我们需要保存一些数据到plist文件中,以下就本人在学习过程中的笔记,不成熟的地方请指出. 可能我有一个类叫做Student import UIKit class Student: NSO ...

  8. 浏览器兼容性工具 Spoon Browser Sandbox

    1.Spoon Browser Sandbox 勺浏览器沙箱 主流浏览器多(IE.Chrome.FireFox.Safari.Opera),浏览器又有很多版本:保证网页在主流浏览器中很好的显示,不可能 ...

  9. struts2 MVC模式

    在学习struts2之前首先了解一下MVC模式. MVC是一个设计模式,它强制性的使应用程序的输入.处理和输出分开.使用MVC应用程序被分成三个核心部件:模型.视图.控制器.它们各自处理自己的任务.分 ...

  10. HDU 4862

    http://acm.hdu.edu.cn/showproblem.php?pid=4862 #include <iostream> #include <cstdio> #in ...