Topcoder--SRM698-Div2 : SubtreeSum
题意 : 给你一棵树,每个节点有点权,树上的每一个联通子图的价值为联通子图中所有节点点权的or和。求这棵树所有联通子图价值和。
显然可以去考虑增量计算,假如我们有了一棵树,在这棵树上加入一个节点会对答案有多少贡献呢?
考虑对于多出来的联通子图,一定且必须包含有新加入的这个节点才是一个没被计算过的联通子图
把加入的这个节点点权拆成二进制来看,对于每一个二进制位为1的位,每个包含该节点的联通子图都有贡献
对于每个二进制为0的位,只有在包含另一个这个位为1的节点时才有贡献
所以可以设计出一个n^2log的算法
代码 :
class SubtreeSum { public: #define MOD 1000000007 int head[55],cnt,n;long long w[55]; bool open[55];long long ret;long long k; struct Edge{ int to,next; }e[205]; inline void insert(int a,int b) { e[++cnt].next=head[a];head[a]=cnt;e[cnt].to=b; e[++cnt].next=head[b];head[b]=cnt;e[cnt].to=a; } void init() { memset(head,0,sizeof(head));cnt=0; ret=0; } bool vis[55]; int Cdfs(int v) { long long ret=1;vis[v]=1; for(int i=head[v];i;i=e[i].next) if(!vis[e[i].to]) ret=ret*(Cdfs(e[i].to)+1)%MOD; return ret; } int comp(int bit,int v) { memset(vis,0,sizeof(vis)); for(int i=0;i<=n;i++) if(!open[i]||w[i]>>bit&1) vis[i]=1; vis[v]=0; return Cdfs(v); } void dfs(int v) { for(int j=0;j<30;j++) if(w[v]>>j&1) { k=comp(31,v);k*=1<<j; k%=MOD;ret=(ret+k)%MOD; } else { k=comp(31,v)-comp(j,v);k*=1<<j; k%=MOD;ret=(ret+k)%MOD; } open[v]=1; for(int i=head[v];i;i=e[i].next) if(!open[e[i].to]) dfs(e[i].to); } int getSum(vector<int> p, vector<int> x) { init(); n=p.size(); for(int i=0;i<n;i++) insert(p[i],i+1); for(int i=0;i<=n;i++) w[i]=x[i]; dfs(0); return ret; } };