您的位置:首页 > 其它

HDU - 5909 Tree Cutting 树形dp + fwt 优化异或卷积和

2017-10-15 11:50 453 查看
题意:

给你一颗n个结点树,每个结点的权值为 vi (0<=vi<m,m<=210),定义一个子树的权值为这个子树节点的权值的异或。分别求权值为[0,m−1]的子树的个数mod(109+7)

思路:

考虑dp[x][val] 表示以x为根,异或权值为val的子树的个数。

显然转移时有:

dp[x][val]=dp[x][val]+∑i⊕jdp[x][i]∗dp[v][j] (卷积异或和).

如果朴素的枚举两个再加上dfs,复杂度为n3.

上面这个卷积异或和的过程我们考虑用fwt优化. O(mlogm)

所以总的复杂度为 O(nmlogm)

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1e3 + 5;
const int maxm = 1500;
const int maxval = (1 << 10);
const int mod = 1e9+7;
typedef long long ll;
int n,m;
int v[maxn];
ll ans[maxm],mid[maxm];
ll dp[maxn][maxm];
vector<int>vt[maxn];

ll qmod (ll a,ll b)
{
ll res = 1;
while(b)
{
if(b&1) res = res * a % mod;
b >>= 1;
a = a * a %mod;
}
return res;
}
ll inv = qmod(2,mod-2);

void FWT_XOR(ll  *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++)
{
ll x=a[i+j],y=a[i+j+d];
a[i+j] = (x+y)%mod,a[i+j+d] = (x-y+mod)%mod;
}
}

void UFWT_XOR(ll *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++)
{
ll x=a[i+j],y=a[i+j+d];

a[i+j]=(x+y)*inv%mod,a[i+j+d]=((x-y)*inv % mod+mod)%mod;
}
}

void solve_XOR(ll *a,ll *b,int maxl)
{
FWT_XOR(a,maxl);
FWT_XOR(b,maxl);
for(int i=0;i < maxl;i++) a[i]=a[i]*b[i] %mod;
UFWT_XOR(a,maxl);
}

void dfs(int x,int f)
{
dp[x][v[x]] = 1;
for(int i = 0;i < vt[x].size();++i)
{
int v = vt[x][i];
if(v == f) continue;
dfs(v,x);
for(int i = 0;i < m;++i)
mid[i] = dp[x][i];
solve_XOR(dp[x],dp[v],maxval);
for(int i = 0;i < m;++i)
dp[x][i] = (dp[x][i] + mid[i]) % mod;
}
for(int i = 0;i < m;++i)
ans[i] = (ans[i] + dp[x][i]) %mod;
}
int main()
{
int _;
cin >> _;
while(_--)
{
scanf("%d %d",&n,&m);
for(int i = 1;i <= n;++i)
scanf("%d",&v[i]),vt[i].clear();
for(int i = 1;i < n;++i)
{
int x,y;
scanf("%d %d",&x,&y);
vt[x].push_back(y);
vt[y].push_back(x);
}
memset(dp,0,sizeof dp);
memset(ans,0,sizeof ans);
dfs(1,-1);

for(int i = 0;i < m;++i)
printf("%lld%c",ans[i],i == m - 1? '\n':' ');
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: