[HDU5909]Tree Cutting

vjudge

题意

一棵(n)个节点的树,每个点有一个小于(m)的权值(v_i)。定义一个连通块的权值是这个连通块内所有点的权值异或和。求权值为(0,1...m-1)的连通块各有多少。膜(10^9+7)
(nle1000,m=2^k,kle10)

sol

很简单有一个(O(nm^2))(dp),设(f[i][j])表示以(i)号点为根权值为(j)的连通块个数,有转移:

[f'[u][i]=sum_{jotimes k=i}f[u][j]*f[v][k]$$其中$v$是$u$的一个儿子。 然后这个东西长得和集合卷积一模一样,所以可以$FWT$优化。 注意一个子树是可以不选的,所以$f[i][0]$在处理完$i$这棵子树后要$+1$方便后续处理。 时间复杂度$O(nm)$ # code ```cpp #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int gi(){ int x=0,w=1;char ch=getchar(); while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if (ch=='-') w=0,ch=getchar(); while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return w?x:-x; } const int N = 2e3+5; const int mod = 1e9+7; int T,n,m,to[N],nxt[N],head[N],cnt,f[N][N],ans[N]; void init(){ memset(f,0,sizeof(f)); memset(head,0,sizeof(head)); memset(ans,0,sizeof(ans)); cnt=0; } void link(int u,int v){ to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt; } void fwt(int *P,int opt){ for (int i=1;i<m;i<<=1) for (int p=i<<1,j=0;j<m;j+=p) for (int k=0;k<i;++k){ int x=P[j+k],y=P[j+k+i]; P[j+k]=1ll*opt*(x+y)%mod; P[j+k+i]=1ll*opt*(x-y+mod)%mod; } } void mul(int *a,int *b){ for (int i=0;i<m;++i) a[i]=1ll*a[i]*b[i]%mod; } void dfs(int u,int fa){ fwt(f[u],1); for (int e=head[u];e;e=nxt[e]) if (to[e]!=fa) dfs(to[e],u),mul(f[u],f[to[e]]); fwt(f[u],(mod+1)/2);(f[u][0]+=1)%=mod;fwt(f[u],1); } int main(){ T=gi(); while (T--){ n=gi();m=gi();init(); for (int i=1;i<=n;++i) ++f[i][gi()]; for (int i=1,u,v;i<n;++i) u=gi(),v=gi(),link(u,v),link(v,u); dfs(1,0); for (int i=1;i<=n;++i){ fwt(f[i],(mod+1)/2);(f[i][0]+=mod-1)%=mod; for (int j=0;j<m;++j) (ans[j]+=f[i][j])%=mod; } printf("%d",ans[0]); for (int i=1;i<m;++i) printf(" %d",ans[i]); puts(""); } return 0; } ```]

原文地址:https://www.cnblogs.com/zhoushuyu/p/9078047.html