题:http://acm.hdu.edu.cn/showproblem.php?pid=6268
题意:给定n个点的树,和一个数m,求树中是否子图点权和为1~m
分析:关键是找subgraph,平时的点分治都是找路径的,这时我们可以结合bitset,每个节点都是一个bitset,每次父亲u保留自己状态,然后把自己状态给儿子v,再让儿子取延申,最后和u的bitset合并,也就顺理成章地表示为包含u的子图能合成的数的集合,再|给答案即可
#include<bits/stdc++.h> using namespace std; const int M=3e3+5; const int N=1e5+5; const int inf=0x3f3f3f3f; typedef long long ll; int n,m; ll sz[M]; int val[M],vis[M],head[M],reco,total,tot,root; struct node{ int v,nextt; ll w; }e[M<<1]; void addedge(int u,int v){ e[tot].v=v; e[tot].nextt=head[u]; head[u]=tot++; } void dfsroot(int u,int f){ sz[u]=1; ll maxson=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ dfsroot(v,u); sz[u]+=sz[v]; maxson=max(maxson,sz[v]); } } maxson=max(maxson,total-sz[u]); if(maxson<reco) root=u,reco=maxson; } bitset<N>b[M],ans; void calc(int u,int f){ b[u]<<=val[u]; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v==f||vis[v]) continue; b[v]=b[u]; calc(v,u); b[u]|=b[v]; } } void solve(int u,int f){ vis[u]=1; b[u].reset();b[u].set(0); calc(u,f); ans|=b[u]; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ total=sz[v]; reco=inf; dfsroot(v,u); solve(root,0); } } } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); ans.reset(); tot=0; for(int i=1;i<=n;i++) head[i]=-1,vis[i]=0; for(int u,v,i=1;i<n;i++){ scanf("%d%d",&u,&v); addedge(u,v),addedge(v,u); } for(int i=1;i<=n;i++) scanf("%d",&val[i]); total=n; reco=inf; dfsroot(1,0); solve(root,0); for(int i=1;i<=m;i++) if(ans[i]==1) putchar('1'); else putchar('0'); puts(""); } return 0; }