zoukankan      html  css  js  c++  java
  • [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[ egin{matrix} F_{i+1}&G_{i+1}&1 end{matrix} ight] * left[ egin{matrix} f_i&f_i&0\ 0&1&0\ f_i&f_i+g_i&1 end{matrix} ight] =left[ egin{matrix} F_{i}&G_{i}&1 end{matrix} ight] ]

    考虑合并矩阵

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

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

    定义Info类记录矩阵

    动态DP

    复杂度(QMlog^2N)

    可以通过

    考虑细节

    细节一

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

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

    所以需要求出逆元

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

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

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

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

    比如最值可以用multiset维护

    所以直接修改线段树即可

    并不会成为复杂度瓶颈

    但是因为每个点都要开

    所以必须动态开点

    细节二

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

    直接取出答案就行了

    不需要实现矩阵乘法

    这是套路

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

    细节三

    因为是从下往上的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
    ",Query(k));
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    iOS sqlite数据库使用
    vsts 自动部署到Azure
    中国区的Azure添加到 VSTS 的 Service Endpoint
    修改vs17中的cordova模板
    升级vs17中的cordova-simulate
    cordova 从xcode7迁移到xcode8
    自杀程序&递归删除目录
    如何升级cordova插件
    在ubuntu on windows 上安装jekyll
    gitphp日期乱码解决方案
  • 原文地址:https://www.cnblogs.com/yicongli/p/10438052.html
Copyright © 2011-2022 走看看