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
 */
posted @ 2021-10-25 17:09  Qquun  阅读(70)  评论(0)    收藏  举报