您的位置:首页 > 其它

HDU 5909 Tree Cutting 树形DP+快速沃尔什变换

2016-10-14 16:11 423 查看
题目大意:给出一棵树,每个点有一个点权,求对于每个i∈[0,m)输出有多少个连通诱导子图的异或和为i

n≤1000,m<210

别问我为什么隔了这么久突然跑回来更blog……我只是在填以前剩下的坑而已。。。

(我花了一整个高三去打游戏,然后花了一整个大一补高三的内容,到了大二,我退学了2333)

FWT

定义:

对于一个长为n=2k的数组A,定义A0为这个数组的前2k−1项,A1为这个数组的后2k−1项,A=(A0,A1)

A±B={A[0]±B[0],A[1]±B[1],...,A[n−1]±B[n−1]}

A∗B={A[0]∗B[0],A[1]∗B[1],...,A[n−1]∗B[n−1]}

A⊕B={∑i⊕j=0A[i]∗B[j],...,∑i⊕j=n−1A[i]∗B[j]}

=(A0⊕B0+A1⊕B1,A0⊕B1+A1⊕B0)

易证⊕运算满足交换律和结合律,且由乘法分配律易得:

A⊕(B+C)=A⊕B+A⊕C

定义Fwt(A)为定义在数组A上的一个运算,定义如下:

Fwt(A)={(Fwt(A0+A1),Fwt(A0−A1))An>1n=1

性质1:

Fwt(A±B)=Fwt(A)±Fwt(B)

证明:容易发现Fwt(A)的每一项都是A[0],A[1],...,A[n−1]的一个线性组合,故对加法满足分配律

性质2:

Fwt(A⊕B)=Fwt(A)∗Fwt(B)

证明:数学归纳法

n=1时显然成立

设该公式对于长度n/2的数组均成立,则:

Fwt(A⊕B)

=Fwt(A0⊕B0+A1⊕B1,A0⊕B1+A1⊕B0)

=(Fwt(A0⊕B0+A1⊕B1+A0⊕B1+A1⊕B0) ,

Fwt(A0⊕B0+A1⊕B1−A0⊕B1−A1⊕B0) )

=(Fwt((A0+A1)⊕(B0+B1)),Fwt((A0−A1)⊕(B0−B1)) )

=(Fwt(A0+A1)∗Fwt(B0+B1),Fwt(A0−A1)∗Fwt(B0−B1))

=(Fwt(A)0∗Fwt(B)0,Fwt(A)1∗Fwt(B)1)

=Fwt(A)∗Fwt(B)

公式真尼玛长- - 是我证麻烦了么- -

这样我们就可以在O(n)的时间内计算A⊕B了……等等

逆运算呢?

……

Dwt(Fwt(A))=Dwt(Fwt(A0+A1),Fwt(A0−A1))

=Dwt(Fwt(A0)+Fwt(A1),Fwt(A0)−Fwt(A1))

令Dwt(A)=(Dwt(A0+A12),Dwt(A0−A12))



Dwt(Fwt(A))=(Dwt(Fwt(A0)),Dwt(Fwt(A1)))

=(A0,A1)=A

完美。

变换时间复杂度O(nlogn),计算时间复杂度O(n),逆变换时间复杂度O(nlogn)。

回来看题,令f[x][j]表示第x个点为根的连通诱导子图中异或和为j的数量,那么f[x]={0,0,...,0,1,0,...}⊕(f[y]+{1,0,0,...}),其中第一个数组的第a[x]位为1,其余都为0

用Fwt加速运算,时间复杂度O(mlogm+nm)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1100
#define MOD 1000000007
using namespace std;

struct edge{
int to,next;
}table[M<<1];
int head[M],tot;
void Add(int x,int y)
{
table[++tot].to=y;
table[tot].next=head[x];
head[x]=tot;
}

int n,m,d;

void FWT(int a[],int n,int type/*1-FWT,-1-DWT*/)
{
if(n==1) return ;
for(int i=0;i<n>>1;i++)
{
a[i]+=a[i+(n>>1)];
a[i+(n>>1)]=a[i]-(a[i+(n>>1)]<<1);
a[i]%=MOD;(a[i+(n>>1)]+=MOD)%=MOD;
if(type==-1)
{
a[i]=(MOD+1ll>>1)*a[i]%MOD;
a[i+(n>>1)]=(MOD+1ll>>1)*a[i+(n>>1)]%MOD;
}
}
FWT(a,n>>1,type);FWT(a+(n>>1),n>>1,type);
}

struct abcd{
int a[M];
abcd() {}
abcd(bool)
{
memset(a,0,sizeof a);
}
int& operator [] (int x)
{
return a[x];
}
void FWT(int type)
{
::FWT(a,d,type);
}
friend abcd operator + (abcd x,abcd y)
{
abcd z(true);
for(int i=0;i<d;i++)
z[i]=(x[i]+y[i])%MOD;
return z;
}
friend abcd operator * (abcd x,abcd y)
{
abcd z(true);
for(int i=0;i<d;i++)
z[i]=((long long)x[i]*y[i])%MOD;
return z;
}
}f[M],zero,ans;

void Initialize()
{
for(d=1;d<m;d<<=1);
memset(f,0,sizeof f);
memset(&zero,0,sizeof zero);
memset(&ans,0,sizeof ans);
zero[0]=1;zero.FWT(1);
memset(head,0,sizeof head);
tot=1;
}

void Tree_DP(int x,int from)
{
for(int i=head[x];i;i=table[i].next)
if(table[i].to!=from)
{
Tree_DP(table[i].to,x);
f[x]=f[x]*(f[table[i].to]+zero);
}
ans=ans+f[x];
}

int main()
{
int T;

for(cin>>T;T;T--)
{
cin>>n>>m;
Initialize();
for(int i=1,x;i<=n;i++)
{
scanf("%d",&x);
f[i][x]=1;
f[i].FWT(1);
}
for(int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
Add(x,y);Add(y,x);
}
Tree_DP(1,0);
ans.FWT(-1);
for(int i=0;i<m;i++)
printf("%d%c",ans[i],i==m-1?'\n':' ');
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息