HDU 5909 Tree Cutting

传送门

题意:

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

Sample Input

2

4 4

2 0 1 3

1 2

1 3

1 4

4 4

0 1 3 1

1 2

1 3

1 4

Sample Output

3 3 2 3

2 4 2 3

令f[i][j]表示以i为根的子树中异或和为j的联通块个数,v为i儿子

f[i][j]+=f[i][k]*f[v][l]    (k^l==j)

发现转移其实可以写成这种形式:

$C_i=\sum_{j^k=i}A_j*B_k$

这和卷积有点类似,不过运算改成了异或

这里就要用到FWT(快速沃尔什变换)

链接1

链接2

就可以做到nlogn转移

转移完后记得在加上原来的f[i][j],因为你可以不选v

复杂度为$O(n^{2}logn)$

卡常,少取模,不要定义long long变量

这题还可以点分治

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<cstring>
  5 #include<algorithm>
  6 using namespace std;
  7 struct Node
  8 {
  9   int next,to;
 10 }edge[2501];
 11 int num,head[1501],Mod=1e9+7,inv2,tmp[2001],a[1001][2001],ans[2001],n,m;
 12 int gi()
 13 {
 14   char ch=getchar();
 15   int x=0;
 16   while (ch<‘0‘||ch>‘9‘) ch=getchar();
 17   while (ch>=‘0‘&&ch<=‘9‘)
 18     {
 19       x=x*10+ch-‘0‘;
 20       ch=getchar();
 21     }
 22   return x;
 23 }
 24 void add(int u,int v)
 25 {
 26   num++;
 27   edge[num].next=head[u];
 28   head[u]=num;
 29   edge[num].to=v;
 30 }
 31 int qpow(int x,int y)
 32 {
 33   int res=1;
 34   while (y)
 35     {
 36       if (y&1) res=1ll*res*x%Mod;
 37       x=1ll*x*x%Mod;
 38       y/=2;
 39     }
 40   return res;
 41 }
 42 void FWT(int *A,int len)
 43 {int i,j,k;
 44   for (i=1;i<m;i<<=1)
 45     {
 46       for (j=0;j<m;j+=(i<<1))
 47     {
 48       for (k=0;k<i;k++)
 49         {
 50           int x=A[j+k],y=A[j+k+i];
 51           A[j+k]=x+y;
 52           if (A[j+k]>=Mod) A[j+k]-=Mod;
 53           A[j+k+i]=x-y+Mod;
 54           if (A[j+k+i]>=Mod) A[j+k+i]-=Mod;
 55         }
 56     }
 57     }
 58 }
 59 void UFWT(int *A,int len)
 60 {int i,j,k;
 61     for (i=1;i<m;i<<=1)
 62     {
 63       for (j=0;j<m;j+=(i<<1))
 64     {
 65       for (k=0;k<i;k++)
 66         {
 67           int x=A[j+k],y=A[j+k+i];
 68           A[j+k]=1ll*(x+y)*inv2%Mod;
 69           A[j+k+i]=1ll*(x-y+Mod)*inv2%Mod;
 70         }
 71     }
 72     }
 73 }
 74 void DP(int x,int y)
 75 {int i;
 76   for (i=0;i<m;i++)
 77     tmp[i]=a[x][i];
 78   FWT(a[x],m);
 79   FWT(a[y],m);
 80   for (i=0;i<m;i++)
 81     a[x][i]=1ll*a[x][i]*a[y][i]%Mod;
 82   UFWT(a[x],m);
 83   for (i=0;i<m;i++)
 84     {
 85       a[x][i]=a[x][i]+tmp[i];
 86       if (a[x][i]>=Mod) a[x][i]-=Mod;
 87     }
 88 }
 89 void dfs(int x,int pa)
 90 {int i;
 91   for (i=head[x];i;i=edge[i].next)
 92     {
 93       int v=edge[i].to;
 94       if (v!=pa)
 95     {
 96       dfs(v,x);
 97       DP(x,v);
 98     }
 99     }
100   for (i=0;i<m;i++)
101     {
102       ans[i]=ans[i]+a[x][i];
103       if (ans[i]>=Mod) ans[i]-=Mod;
104     }
105 }
106 int main()
107 {int T,i,x,u,v,j;
108   cin>>T;
109   inv2=qpow(2,Mod-2);
110   while (T--)
111     {
112       memset(head,0,sizeof(head));
113       num=0;
114       memset(a,0,sizeof(a));
115       memset(ans,0,sizeof(ans));
116       scanf("%d%d",&n,&m);
117       for (i=1;i<=n;i++)
118     {
119       x=gi();
120       a[i][x]=1;
121     }
122       for (i=1;i<=n-1;i++)
123     {
124       u=gi();v=gi();
125       add(u,v);add(v,u);
126     }
127       dfs(1,0);
128       for (i=0;i<m-1;i++)
129       printf("%d ",ans[i]);
130       printf("%d\n",ans[m-1]);
131     }
132 }

原文地址:https://www.cnblogs.com/Y-E-T-I/p/8430651.html

时间: 2024-11-08 23:58:53

HDU 5909 Tree Cutting的相关文章

hdu 5909 Tree Cutting——点分治(树形DP转为序列DP)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5909 点分治的话,每次要做一次树形DP:但时间应该是 siz*m2 的.可以用 FWT 变成 siz*mlogm ,但这里写的是把树变成序列来 DP 的方法,应该是 nlogn*m 的. 树上的一个点,如果选,就可以选它的孩子,所以它向它的第一个孩子连边:如果不选,就会跳到它的下一个兄弟或者是父亲的下一个兄弟之类的,向那边连一条边. 做出树的 dfs 序,把边都连在 dfs 序上:其实那个第一条边一定连

【HDU 5909】 Tree Cutting (树形依赖型DP+点分治)

Tree Cutting 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 v1⊕v2⊕...⊕vn, where ⊕ denotes bitwise-xor. Now fo

hdu 5293 Tree chain problem(树链剖分+树形dp)

题目链接:hdu 5293 Tree chain problem 维护dp[u], sum[u],dp[u]表示以u为根节点的子树的最优值.sum[u]表示以u节点的所有子节点的dp[v]之和.对于边a,b,w,在LCA(a,b)节点的时候进行考虑.dp[u] = min{dp[u], Sum(a,b) - Dp(a,b) + sum[u] | (ab链上的点,不包括u } #pragma comment(linker, "/STACK:1024000000,1024000000")

hdu 5370 Tree Maker(catalan+dp)

题目链接:hdu 5370 Tree Maker n个节点的二叉树种类为Catalan数的第n项 对于一棵子树而言,被移动过的节点就是确定的位置,所以只要知道已经确定位置的K个节点有多少个空孩子指针M,和就该子树下的N个未确定位置的节点,等于是说用N个节点构造M个可为空的子树的种类数.对于整个树的形态数即为若干棵独立的子树形态数的乘积. 定义dp[i][j]为用i个节点构造j棵树的形态数,dp[i][j] = sum{ dp[i-1][j-k] * catalan[k] | 0 ≤ k ≤j }

3391: [Usaco2004 Dec]Tree Cutting网络破坏

3391: [Usaco2004 Dec]Tree Cutting网络破坏 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 76  Solved: 59[Submit][Status][Discuss] Description 约翰意识到贝茜建设网络花费了他巨额的经费,就把她解雇了.贝茜很愤怒,打算狠狠报 复.她打算破坏刚建成的约翰的网络.    约翰的网络是树形的,连接着N(1≤N≤10000)个牛棚.她打算切断某一个牛棚的电源,使和这个牛棚相连的

HDU 5044 Tree(树链剖分)

HDU 5044 Tree 题目链接 就简单的树链剖分,不过坑要加输入外挂,还要手动扩栈 代码: #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int N = 100005; #pragma comment(linker, "/STACK:1024000000,1024000000"

hdu 4757 Tree(可持久化字典树)

题目链接:hdu 4757 Tree 题目大意:给定一棵树,每一个节点有一个值.如今有Q次询问,每次询问u到v路径上节点值与w亦或值的最大值. 解题思路:刚開始以为是树链剖分,事实上树链剖分仅仅是用来求LCA(能够不用树链剖分). 可持久化字典树.在每次插入的同一时候,不改动原先的节点.而是对全部改动的节点复制一个新的节点,而且在新的节点 上做操作,这样做的目的是可以获取某次改动前的状态.同过可持久化的操作,保留了改动前后的公共数据. 对给定树上的全部节点权值建立01字典树,然后每一个节点都保存

fwt优化+树形DP HDU 5909

1 //fwt优化+树形DP HDU 5909 2 //见官方题解 3 // BestCoder Round #88 http://bestcoder.hdu.edu.cn/ 4 5 #include <bits/stdc++.h> 6 // #include <iostream> 7 // #include <cstdio> 8 // #include <cstdlib> 9 // #include <algorithm> 10 // #inc

hdu 3534 Tree(树形DP)

题目链接:hdu 3534 Tree 题意: 给你一棵n个节点,n-1条边的树,每条边有一个长度,现在问你最长的边的长度为多少,有多少条. 题解: 其实这种题不用记录最长和次长,我们开两个数组,len[i],num[i]. 表示以i为根结点出发的最长的长度以及最长的边的条数. 然后我们只需要一个dfs,先用子节点的信息来更新答案,然后在更新当前节点的len和num记录的信息. 这样就不用记录最长和次长. 1 #include<bits/stdc++.h> 2 #define mst(a,b)