題目傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=5909
題目大意:給出一棵樹,每個節點的權值都在 內。現在對於每個 ,你都應給出權值爲 的至少有一個點的子連通塊個數。一個連通塊的權值定義爲其所有點的權值異或和。答案模 ,多組數據。
題目分析:首先很容易想到 的樹形DP。由於樹的一個子連通塊也是一棵樹,可以使1號點爲整棵樹的根,定義子連通塊的根爲其中深度最小的節點。然後令f[node][v]表示以node爲根,異或和爲v的子連通塊有多少個。顯然,當逐個考慮node的兒子的時候,考慮新的兒子son對f[node][v]的貢獻,發現有以下形式的轉移:
其中 是異或操作。
這個方程顯然可以用FWT優化,這樣 就可以通過所有數據。然而這樣常數很大,方法也不夠巧妙,有沒有別的優化方法呢?
我們發現本題是詢問關於樹上的連通塊問題,因爲不知道連通塊的根,所以要對每個節點DP。假設強制這個連通塊過根,能不能降低DP的時間複雜度呢?我們又發現:如果這個連通塊過根,那麼若一個點不選,它的子樹也不能選;否則它的子樹纔有可能選。如果定義一個指針指向當前考慮的節點的話,那麼前者就表示這個指針移向了DFS序上該子樹對應區間的右一位,而後者就代表指針直接移向DFS序的下一位。這樣按DFS序去DP,時間就是 的。
那麼如何處理不過根的問題呢?只要套個點分治就行了。這樣總時間複雜度是 的,不僅方法優美而且十分好寫。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1050;
const long long M=1000000007;
typedef long long LL;
struct edge
{
int obj;
edge *Next;
} e[maxn<<1];
edge *head[maxn];
int cur;
int tree[maxn];
int num;
int Size[maxn];
int max_Size[maxn];
int dfsx[maxn];
int st[maxn];
int ed[maxn];
LL f[maxn][maxn];
LL ans[maxn];
int val[maxn];
bool vis[maxn];
int t,n,m;
void Add(int x,int y)
{
cur++;
e[cur].obj=y;
e[cur].Next=head[x];
head[x]=e+cur;
}
void Dfs1(int node,int fa)
{
num++;
tree[num]=node;
Size[node]=1;
max_Size[node]=0;
for (edge *p=head[node]; p; p=p->Next)
{
int son=p->obj;
if ( son==fa || vis[son] ) continue;
Dfs1(son,node);
Size[node]+=Size[son];
max_Size[node]=max(max_Size[node],Size[son]);
}
}
void Dfs2(int node,int fa)
{
num++;
dfsx[num]=node;
st[node]=num;
for (edge *p=head[node]; p; p=p->Next)
{
int son=p->obj;
if ( son==fa || vis[son] ) continue;
Dfs2(son,node);
}
ed[node]=num;
}
void Solve(int node)
{
num=0;
Dfs1(node,node);
if (num==1)
{
ans[ val[node] ]=(ans[ val[node] ]+1)%M;
return;
}
int root=tree[1];
for (int i=1; i<=num; i++)
{
int x=tree[i];
Size[x]=max(Size[node]-Size[x],max_Size[x]);
if (Size[x]<Size[root]) root=x;
}
num=0;
Dfs2(root,root);
for (int i=2; i<=num+1; i++)
for (int j=0; j<m; j++) f[i][j]=0;
f[2][ val[ dfsx[1] ] ]=1;
for (int i=2; i<=num; i++)
for (int j=0; j<m; j++)
{
LL &x=f[i+1][ j^val[ dfsx[i] ] ];
x=(x+f[i][j])%M;
LL &y=f[ ed[ dfsx[i] ]+1 ][j];
y=(y+f[i][j])%M;
}
for (int i=0; i<m; i++) ans[i]=(ans[i]+f[num+1][i])%M;
vis[root]=true;
for (edge *p=head[root]; p; p=p->Next)
{
int son=p->obj;
if (vis[son]) continue;
Solve(son);
}
}
int main()
{
freopen("5909.in","r",stdin);
freopen("5909.out","w",stdout);
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
head[i]=NULL,vis[i]=false,scanf("%d",&val[i]);
cur=-1;
for (int i=1; i<n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
Add(x,y);
Add(y,x);
}
for (int i=0; i<m; i++) ans[i]=0;
Solve(1);
for (int i=0; i<m-1; i++) printf("%d ",ans[i]);
printf("%d\n",ans[m-1]);
}
return 0;
}