[SDOI2017]切树游戏

题面

这道题从昨天下午写到今天下午

我好菜啊

感到绝望

算法一

考虑朴素dp

\(dp[x][i]\)表示以\(x\)为根的子树中权值\(i\)的子树个数

考虑加入子节点\(y\)的转移

\[dp[x][k]+=\sum_{i~xor~j=k}{dp[x][i]*dp[y][j]} \]

答案即为

\[\sum_{i=1}^{n}{dp[i][k]} \]

复杂度\(O(QNM^2)\)

算法二

注意到可以使用FWT优化

\(DP[x]\)表示\(FWT(dp[x])\)

\[DP[x]+=DP[x]*DP[y] \]

\[DP[x]*=DP[y]+1 \]

注意因为已经\(FWT\)过了

所以这里的乘法是按位相乘

所以那个\(1\)指的是一个全\(1\)的数组

定义Poly类记录\(128\)个位置的\(dp\)

FWT后计算

最后IFWT回来

复杂度\(O(QNMlogM)\)

算法三

发现这是个动态DP

\(dfn[x]\)表示\(x\)\(dfs\)

\(F[dfn[x]]\)表示\(x\)DP

\[F[dfn[x]]=\prod_{y~is ~son~of~x}{(F[dfn[y]]+1)} \]

\(f[dfn[x]]\)表示\(x\)轻儿子的DP

\[f[dfn[x]]=\prod_{y~is~light~son~of~x}{(F[dfn[y]]+1)} \]

\(G[dfn[x]]\)表示\(x\)子树内的答案

\[G[dfn[x]]=\sum_{y~is~son~of~x}G[dfn[y]] \]

\(g[dfn[x]]\)表示\(x\)轻子树内的答案

\[g[dfn[x]]=\sum_{y~is~light~son~of~x}{G[dfn[y]]} \]

注意以上数组经过FWT后计算

\(y​\)\(x​\)的重儿子

有转移

\[F[dfn[x]]=f[dfn[x]]*(F[dfn[y]]+1)\\ G[dfn[x]]=g[dfn[x]]+F[dfn[x]]+G[dfn[y]] \]

\(dfn[x]=i\)那么\(dfn[y]=i+1\)

\[F[i]=f[i]*F[i+1]+f[i]\\ G[i]=g[i]+f[i]*F[i+1]+f[i]+G[i+1]\\ \]

写成矩阵就是

\[\left[ \begin{matrix} F_{i+1}&G_{i+1}&1 \end{matrix} \right] * \left[ \begin{matrix} f_i&f_i&0\\ 0&1&0\\ f_i&f_i+g_i&1 \end{matrix} \right] =\left[ \begin{matrix} F_{i}&G_{i}&1 \end{matrix} \right] \]

考虑合并矩阵

\[\left[ \begin{matrix} a&b&0\\ 0&1&0\\ c&d&1 \end{matrix} \right]* \left[ \begin{matrix} A&B&0\\ 0&1&0\\ C&D&1 \end{matrix} \right] = \left[ \begin{matrix} aA&aB+b&0\\ 0&1&0\\ cA+C&cB+d+D&1 \end{matrix} \right] \]

发现只需记录\(4\)个位置的值即可

定义Info类记录矩阵

动态DP

复杂度\(QMlog^2N\)

可以通过

考虑细节

细节一

修改\(x\)的时候\(f[dfn[fa[x]]]\)需要除去原来的贡献

因为是在\(\mod 10007\)意义下求解

所以需要求出逆元

然而\(0\)(\(10007\)的倍数)没有逆元

似乎有一种记录\(0\)个数的做法

但是更自然的想法是对每个节点维护一颗线段树专门维护\(f​\)数组

维护像这种不可减的信息都是这样用数据结构维护的

比如最值可以用multiset维护

所以直接修改线段树即可

并不会成为复杂度瓶颈

但是因为每个点都要开

所以必须动态开点

细节二

最后统计答案乘上初始矩阵\(\left[\begin{matrix}0&0&1 \end{matrix} \right]​\)

直接取出答案就行了

不需要实现矩阵乘法

这是套路

\[\left[\begin{matrix}0&0&1 \end{matrix} \right]* \left[ \begin{matrix} a&b&0\\ 0&1&0\\ c&d&1 \end{matrix} \right] =\left[\begin{matrix}c&d&1 \end{matrix} \right] \]

细节三

因为是从下往上的dp

也就是线段树是反着维护矩阵的

这是套路

#include<bits/stdc++.h>

using namespace std;

#define gc c=getchar()
#define r(x) read(x)
#define ll long long

template<typename T>
inline void read(T&x){
    x=0;T k=1;char gc;
    while(!isdigit(c)){if(c=='-')k=-1;gc;}
    while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}

const int N=3e4+7;
const int M=128|7;
const int p=10007;

inline int add(int a,int b){
	return (a+=b)>=p?a-p:a;
}

inline int sub(int a,int b){
	return (a-=b)<0?a+p:a;
}

inline int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)(ans*=a)%=p;
		(a*=a)%=p;
		b>>=1;
	}
	return ans;
}

int n,m;
int Inv;

struct Poly{
	
	int A[M];
	
	inline void FWT(){
		for(int i=1;i<m;i<<=1){
			for(int j=0;j<m;j+=(i<<1)){
				for(int k=0;k<i;++k){
					int u=A[j+k],v=A[i+j+k];
					A[j+k]=add(u,v),A[i+j+k]=sub(u,v);
				}
			}
		}
	}
	
	inline void IFWT(){
		FWT();
		for(int i=0;i<m;++i){(A[i]*=Inv)%=p;}
	}
	
	inline Poly(){}
	
	inline Poly(int x){
		memset(A,0,m<<2);
		A[x]=1;
		FWT();
	}
	
	inline int& operator [] (const int &x){return A[x];}
	
	inline const int& operator [] (const int &x)const {return A[x];}
	
	inline void operator =(const Poly &T){
		memcpy(A,T.A,m<<2);
	}
	
	inline void operator *= (const Poly &T){
		for(int i=0;i<m;++i)(A[i]*=T[i])%=p;
	}
	
	inline Poly operator * (const Poly &T)const {
		Poly ret;
		for(int i=0;i<m;++i)ret[i]=A[i]*T[i]%p;
		return ret;
	}
	
	inline void operator += (const Poly &T){
		for(int i=0;i<m;++i)A[i]=add(A[i],T[i]);
	}
	
	inline Poly operator +(const Poly &T)const {
		Poly ret;
		for(int i=0;i<m;++i)ret[i]=add(A[i],T[i]);
		return ret;
	}
	
	inline void operator -= (const Poly &T){
		for(int i=0;i<m;++i)A[i]=sub(A[i],T[i]);
	}
	
	inline Poly operator -(const Poly &T)const {
		Poly ret;
		for(int i=0;i<m;++i)ret[i]=sub(A[i],T[i]);
		return ret;
	}
}one,F[N],G[N],g[N];

int root[N];
int cnt[N];

namespace Data{
	int tot;
	Poly tr[N*10];
	int ls[N*10];
	int rs[N*10];
	int siz[N*10];
	#define ls ls[rt]
	#define rs rs[rt]
	
	inline void update(int rt){
		tr[rt]=tr[ls]*tr[rs];
	}
	
	void insert(int &rt,int l,int r,int x,const Poly &v){
		if(!rt)rt=++tot;
		++siz[rt];
		if(l==r)return void(tr[rt]=v);
		int mid=(l+r)>>1;
		if(x<=mid)insert(ls,l,mid,x,v);
		else insert(rs,mid+1,r,x,v);
		if(siz[rt]>r-l)update(rt);
	}
	#undef ls
	#undef rs
};

int a[N];

vector<int>E[N];

int fa[N],siz[N],dep[N],son[N];

void dfs1(int x,int f){
	fa[x]=f;
	siz[x]=1;
	dep[x]=dep[f]+1;
	for(int i=0;i<E[x].size();++i){
		int v=E[x][i];
		if(v==f)continue;
		dfs1(v,x);
		siz[x]+=siz[v];
		if(siz[son[x]]<siz[v])son[x]=v;
	}
}

int dfs_clock;
int top[N],bot[N],dfn[N],ptn[N];

void dfs2(int x,int t){
	top[x]=t;
	dfn[x]=++dfs_clock;
	ptn[dfs_clock]=x;
	if(!son[x])return void(bot[x]=x);
	dfs2(son[x],t);
	bot[x]=bot[son[x]];
	for(int i=0;i<E[x].size();++i){
		int v=E[x][i];
		if(v==fa[x]||v==son[x])continue;
		dfs2(v,v);
	}
}

int pos[N];

void dfs3(int x){
	F[x]=Poly(a[x]);
	for(int i=0;i<E[x].size();++i){
		int v=E[x][i];
		if(v==fa[x])continue;
		dfs3(v);
		F[x]*=F[v]+one;
		G[x]+=G[v];
	}
	G[x]+=F[x];
	for(int i=0;i<E[x].size();++i){
		int v=E[x][i];
		if(v==fa[x]||v==son[x])continue;
		pos[v]=++cnt[x];
	}
	cnt[x]++;
	Data::insert(root[x],1,cnt[x],cnt[x],Poly(a[x]));
	for(int i=0;i<E[x].size();++i){
		int v=E[x][i];
		if(v==fa[x]||v==son[x])continue;
		g[x]+=G[v];
		Data::insert(root[x],1,cnt[x],pos[v],F[v]+one);
	}
}

struct Info{
	Poly a,b,c,d;
	
	inline Info(){};
	inline Info(const Poly &f,const Poly &g):a(f),b(f),c(f),d(f+g){};
	inline Info(const Poly &A,const Poly &B,const Poly &C,const Poly &D):a(A),b(B),c(C),d(D){}
	
	inline Info operator + (const Info &x){
		return Info(a*x.a,a*x.b+b,c*x.a+x.c,c*x.b+d+x.d);
	}
	
	inline Poly f(){
		return c;
	}
	
	inline Poly g(){
		return d;
	}
	
}tr[N<<2];

#define ls (rt<<1)
#define rs (rt<<1|1)

inline void update(int rt){
	tr[rt]=tr[rs]+tr[ls];
}

void build(int rt,int l,int r){
	if(l==r){
		int x=ptn[l];
		tr[rt]=Info(Data::tr[root[x]],g[x]);
		return ;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	update(rt);
}

void modify(int rt,int l,int r,int x,const Info &v){
	if(l==r){
		tr[rt]=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)modify(ls,l,mid,x,v);
	else modify(rs,mid+1,r,x,v);
	update(rt);
}

Info query(int rt,int l,int r,int x,int y){
	if(x<=l&&r<=y)return tr[rt];
	int mid=(l+r)>>1;
	if(y<=mid)return query(ls,l,mid,x,y);
	if(x>mid)return query(rs,mid+1,r,x,y);
	return query(rs,mid+1,r,x,y)+query(ls,l,mid,x,y);
}

inline void Change(int x,int y){
	a[x]=y;
	Data::insert(root[x],1,cnt[x],cnt[x],Poly(a[x]));
	while(x){
		modify(1,1,n,dfn[x],Info(Data::tr[root[x]],g[x]));
		x=top[x];
		Info tmp=query(1,1,n,dfn[x],dfn[bot[x]]);
		if(fa[x])Data::insert(root[fa[x]],1,cnt[fa[x]],pos[x],Poly(tmp.f()+one));
		if(fa[x])g[fa[x]]-=G[x];
		G[x]=tmp.g();
		if(fa[x])g[fa[x]]+=G[x];
		x=fa[x];
	}
}

inline int Query(int x){
	Poly t=query(1,1,n,dfn[1],dfn[bot[1]]).g();
	t.IFWT();
	return t[x];
}

inline void init(){
	r(n),r(m);Inv=qpow(m,p-2);
	for(int i=0;i<m;++i)one[i]=1;
	for(int i=1;i<=n;++i){
		r(a[i]);
	}
	for(int i=1;i<n;++i){
		int u,v;r(u),r(v);
		E[u].push_back(v);
		E[v].push_back(u);
	}
	dfs1(1,0);
	dfs2(1,1);
	dfs3(1);
	build(1,1,n);
}

int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	init();
	int q;r(q);
	while(q--){
		char s[10];
		scanf("%s",s);
		if(s[0]=='C'){
			int x,y;r(x),r(y);
			Change(x,y);
		}
		else {
			int k;r(k);
			printf("%d\n",Query(k));
		}
	}
	return 0;
}

posted @ 2019-02-26 16:11  NamelessOIer  阅读(322)  评论(1编辑  收藏  举报