HDU 5909 Tree Cutting (树形dp+FWT)

来源:互联网 发布:怎么切换mac系统 编辑:程序博客网 时间:2024/06/06 01:56

Tree Cutting

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K (Java/Others)
Total Submission(s): 183    Accepted Submission(s): 77

Problem Description
Byteasar has a tree T with n vertices conveniently labeled with 1,2,...,n. Each vertex of the tree has an integer value vi.
The value of a non-empty tree T is equal to v1v2...vn, where denotes bitwise-xor.
Now for every integer k from [0,m), please calculate the number of non-empty subtree of T which value are equal to k.
A subtree of T is a subgraph of T that is also a tree.
Input
The first line of the input contains an integerT(1T10), denoting the number of test cases.
In each test case, the first line of the input contains two integers n(n1000) and m(1m210), denoting the size of the tree T and the upper-bound of v.
The second line of the input contains n integers v1,v2,v3,...,vn(0vi<m), denoting the value of each node.

Each of the following n1 lines contains two integers ai,bi, denoting an edge between vertices ai and bi(1ai,bin).

It is guaranteed that m can be represent as 2k, where k is a non-negative integer.

Output
For each test case, print a line withm integers, the i-th number denotes the number of non-empty subtree of T which value are equal to i.
The answer is huge, so please module 109+7.
Sample Input
24 42 0 1 31 21 31 44 40 1 3 11 21 31 4
Sample Output
3 3 2 32 4 2 3

Source
BestCoder Round #88

Recommend
wange2014   |   We have carefully selected several similar problems for you:  5932 5931 5930 5929 5928 

题意:
有一棵n个点的无根树,节点依次编号为1到n,其中节点i的权值为v。
定义一棵树的价值为它所有点的权值的异或和。
现在对于每个[0,m)的整数k,请统计有多少T的非空连通子树的价值等于k。
一棵树T的连通子树就是它的一个连通子图,并且这个图也是一棵树。

题解:设dp[ u ][ i ]表示 u 为根的数,异或后得到 i 的方案数。
转移是个异或卷积的形式,可以用FWT加速计算。
时间复杂度:O(n*m*logm).

那么对于一个状态dp[u][i]我们有加入u的一个子树x之后的新的状态dp[u"][ i ]的值dp[u"][ i ]=dp[u][v]+ (异或卷积)∑(i xor j)dp[u][ i ]*dp[x][ j ]。

AC代码:
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<vector>using namespace std;typedef long long LL;const int N=1e3+100; //2^10const int mod=1e9+7;const int rev=(mod+1)>>1;//dp[u][i]表示 u 为根的数,异或后得到 i 的方案数 int val[N], dp[N][N],ans[N];int temp[N];int len;int n,m;struct Edge{int from,to;}edge[2*N];int cnt;int head[N];vector<int> V[2*N]; //void addedge(int u,int v)//{//edge[cnt].from=v;  edge[cnt].to=head[u];  head[u]=cnt++;//edge[cnt].from=u;  edge[cnt].to=head[v];  head[v]=cnt++;//}void FWT(int a[],int n){    for(int d=1;d<n;d<<=1)        for(int m=d<<1,i=0;i<n;i+=m)            for(int j=0;j<d;j++){                int x=a[i+j],y=a[i+j+d];                a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;                //xor:a[i+j]=x+y,a[i+j+d]=(x-y+mod)%mod;                //and:a[i+j]=x+y;                //or:a[i+j+d]=x+y;            }}void UFWT(int a[],int n){    for(int d=1;d<n;d<<=1)        for(int m=d<<1,i=0;i<n;i+=m)            for(int j=0;j<d;j++){                int x=a[i+j],y=a[i+j+d];                a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;                //xor:a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;                //and:a[i+j]=x-y;                //or:a[i+j+d]=y-x;            }}void solve(int a[],int b[],int n){    FWT(a,n);    FWT(b,n);    for(int i=0;i<n;i++) a[i]=1LL*a[i]*b[i]%mod;    UFWT(a,n);}void dfs(int u,int fa){dp[u][val[u]]=1;//自己与自己异或后为 0 的方案数 for(int i=0;i<V[u].size();i++){int v = V [u][i];if(v==fa) continue;dfs(v,u);for(int i=0;i<m;i++) temp[i]=dp[u][i];solve(dp[u],dp[v],m); //当前dp[u]的所有值与dp[v]的所有值异或的结果for(int i=0;i<m;i++) dp[u][i]=(dp[u][i]+temp[i])%mod;}for(int i=0;i<m;i++) ans[i]=(ans[i]+dp[u][i])%mod;}int main(){int t,u,v; scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);memset(ans,0,sizeof(ans));memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++) V[i].clear(); for(int i=1;i<=n;i++) scanf("%d",&val[i]);for(int i=1;i<n;i++){scanf("%d%d",&u,&v);//addedge(u,v);//addedge(v,u);V[u].push_back(v);V[v].push_back(u);}dfs(1,0);for(int i=0;i<m-1;i++)printf("%d ",ans[i]);printf("%d\n",ans[m-1]);}return 0;}



2 0