HDU6268 Master of Subgraph (点分治+bitset优化dp)
HDU6268 Master of Subgraph
Mean
给定\(n\)个点的一棵树,每个点点权为\(val_i\),定义联通块的权值为联通块内所有点的权值和,问值域\([1,m]\)上的值能否被某个联通块表示。
\(n<=3000,val_i<=100000,m<=100000\).
Sol
点分治+bitset优化dp
先考虑dp,发现如果正常做树上\(01\)背包的话复杂度不允许
进行改进,dp方程还是表示为\(dp[x][i]\)表示经过点\(x\)的联通块能否表示\(i\).
从\(x\)节点开始\(dfs\),搜到一个新的节点\(y\)就要把其父亲的状态全部左移\(val_y\),表示选了这个点,必定选其父亲节点.
回溯\(y\)时再把节点\(y\)的状态与其父亲的状态合并,表示当前父亲能走\(y\)这一分支的所有状态了.
然后继续去搜索其他分支,做同样的操作.
上述左移,或的操作可以用\(bitset\)实现.
注意到这么做要枚举每个点,复杂度是\(O(n^2*(n*m)/w)\)的.
所以就可以套点分治了,复杂度\(O(logn(nm/w+nm/w) )\).
两个\(nm/w\)分别是每个点与最终答案或一次,一共\(n\)个点,左移的总量为所有点的权值和,最坏\(nm\),再除个\(w\)就是第二个了.
点分治的写法和大家不太一样,所以边界处理有些不同,仅供参考。
Code
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
const int N=2e4+10;
const int M=1e5+1;
struct node{
int to;
int next;
int dis;
}edge[N<<1];
int head[N],num_edge,u,v,_d,base;
bool st[N];
int eval[N];
bitset<M>ans,bs[N];
#define debug(x) cout<<#x<<" :"<<x<<endl
void edge_add(int from,int to,int dis){
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
edge[num_edge].dis=dis;
head[from]=num_edge;
}
int get_size(int u,int fa){
if(st[u])return 0;
int res=1;
for(int i=head[u];i;i=edge[i].next){
int to=edge[i].to;
if(to==fa)continue;
res+=get_size(to,u);
}
return res;
}
int get_wc(int u,int fa,int tot,int &wc){
if(st[u])return 0;
int res=1;
int mx=0;
for(int i=head[u];i;i=edge[i].next){
int to=edge[i].to;
if(to==fa)continue;
int val=get_wc(to,u,tot,wc);
res+=val;
mx=max(mx,val);
}
mx=max(mx,tot-res);
if(mx<=tot/2)wc=u;
return res;
}
int conp=0;
int conq=0;
int mp[3],zmp[3];
void get_dist(int u,int fa,int dist,int &conp){
if(st[u])return ;
bs[u]=(bs[fa]<<eval[u]);
ans|=bs[u];
for(int i=head[u];i;i=edge[i].next){
int to=edge[i].to;
if(to==fa||st[to])continue;
get_dist(to,u,dist+edge[i].dis,conp);
bs[u]|=bs[to];
}
}
void cle(int u,int fa,int dist,int &conp){
if(st[u])return ;
bs[u].reset();
for(int i=head[u];i;i=edge[i].next){
int to=edge[i].to;
if(to==fa||st[to])continue;
cle(to,u,dist+edge[i].dis,conp);
}
}
void cal(int u){
if(st[u])return ;
get_wc(u,-1,get_size(u,-1),u);
st[u]=true;
conq=0;
bs[u][eval[u]]=1;
for(int i=head[u];i;i=edge[i].next){
int to=edge[i].to;
get_dist(to,u,edge[i].dis,conp);
bs[u]|=bs[to];
}
ans|=bs[u];
bs[u].reset();
for(int i=head[u];i;i=edge[i].next){
int to=edge[i].to;
cle(to,u,edge[i].dis,conp);
}
for(int i=head[u];i;i=edge[i].next)cal(edge[i].to);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i){
st[i]=0;
bs[i].reset();
head[i]=0;
}
num_edge=0;
for(int i=1;i<=n-1;++i){
int x,y,w;
scanf("%d%d",&x,&y);
edge_add(x,y,1);
edge_add(y,x,1);
}
ans.reset();
for(int i=1;i<=n;++i)scanf("%d",&eval[i]);
for(int i=1;i<=n;++i){
ans[eval[i]]=1;
}
cal(1);
for(int i=1;i<=m;++i){
if(ans[i])printf("1");
else printf("0");
}
puts("");
}
return 0;
}
/*
2
4 10
1 2
2 3
3 4
3 2 7 5
6 10
1 2
1 3
2 5
3 4
3 6
1 3 5 7 9 11
1
2 6
1 2
2 3
*/