• 一类经典的树形染色问题


    题目

    1. 2种做法,一种是 $O(nklog n)$,另一种是 $O(n)$。前者可以从深度大的开始填(优先队列维护)。后者只需要开 $f[0][x]$ 表示 $x$ 离关键点的最近距离,$f[1][x]$ 表示 $x$ 离没被控制的最远点的距离。考虑 $f[0][x]+f[1][x] le k$ ,$x$ 就能被控制。$f[1][x]=k$,$x$ 就要成为关键点。

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <vector>
     6 #include <cmath>
     7 #include <queue>
     8 #include <map>
     9  
    10 #define ll long long
    11  
    12 using namespace std;
    13 int rd() {
    14     int f=1,sum=0; char ch=getchar();
    15     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    16     while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    17     return sum*f;
    18 }
    19 ll lrd() {
    20     ll f=1,sum=0; char ch=getchar();
    21     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    22     while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    23     return sum*f;
    24 }
    25 
    26 const int N=(int)(1e5+5);
    27 struct edge {
    28     int nex,to;
    29 }e[N<<1];
    30 struct node {
    31     int val,id;
    32     bool operator < (const node &x) const {
    33         return val<x.val;
    34     }
    35     bool operator > (const node &x) const {
    36         return val>x.val;
    37     }
    38 };
    39 priority_queue<node>q;
    40 bool vis[N];
    41 int head[N],cnt,dep[N],fa[N];
    42 int n,m;
    43 
    44 void add_edge(int x,int y) {
    45     e[++cnt]=(edge){head[x],y}; head[x]=cnt;
    46 }
    47 
    48 void dfs1(int x,int ff) {
    49     for(int i=head[x];i;i=e[i].nex) {
    50         int y=e[i].to;
    51         if(dep[y]) continue;
    52         fa[y]=x; dep[y]=dep[x]+1;
    53         dfs1(y,x);
    54     }
    55 }
    56 
    57 void dfs2(int x,int ff,int num) {
    58     //cout<<x<<" "<<num<<endl;
    59     vis[x]=1; if(!num) return ;
    60     for(int i=head[x];i;i=e[i].nex) {
    61         int y=e[i].to;
    62         if(y==ff) continue;
    63         dfs2(y,x,num-1);
    64     } 
    65 }
    66 
    67 int main() {
    68     int x,y;
    69     n=rd(); m=rd(); rd();
    70     for(int i=1;i<n;i++) {
    71         x=rd(); y=rd(); add_edge(x,y); add_edge(y,x);
    72     }
    73     fa[1]=1; dep[1]=1; dfs1(1,1);
    74     for(int i=1;i<=n;i++) q.push((node){dep[i],i});
    75     int ans=0;
    76     while(!q.empty()) {
    77         int x=q.top().id; q.pop();
    78         if(vis[x]) continue;
    79         vis[x]=1; ++ans; 
    80         for(int i=1;i<=m;i++) x=fa[x];
    81         dfs2(x,x,m);
    82     }
    83     printf("%d",ans);
    84     return 0;
    85 } 
    View Code

    2. 考虑二分,然后把非关键点就不要计入贡献,即只需要让这些非关键点满足$f[0][x]+f[1][x] le k$ 即可。那么初始化改下 $f[1]$ 就好。 dp 过程对于关键点的 $f[1]$ 要对 0 取个max。即也许最远没被控制的是它自己。

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <vector>
     6 #include <cmath>
     7 #include <queue>
     8 #include <map>
     9 
    10 #define ll long long
    11 
    12 using namespace std;
    13 int rd() {
    14     int f=1,sum=0; char ch=getchar();
    15     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    16     while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    17     return sum*f;
    18 }
    19 ll lrd() {
    20     ll f=1,sum=0; char ch=getchar();
    21     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    22     while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    23     return sum*f;
    24 }
    25 // f[0][x] x到关键点的最小距离 f[1][x] x到最远未控制点的距离 
    26 const int N=(int)(3e5+5);
    27 struct edge {
    28     int nex,to;
    29 }e[N<<1];
    30 int f[2][N],col[N],head[N],cnt,n,m,K,ans;
    31 
    32 void add_edge(int x,int y) {
    33     e[++cnt]=(edge){head[x],y}; head[x]=cnt;
    34 }
    35 
    36 void dfs(int x,int ff) {
    37     for(int i=head[x];i;i=e[i].nex) {
    38         int y=e[i].to;
    39         if(y==ff) continue;
    40         dfs(y,x);
    41         f[1][x]=max(f[1][x],f[1][y]+1);
    42         f[0][x]=min(f[0][x],f[0][y]+1);
    43     }
    44     if(col[x]) f[1][x]=max(f[1][x],0);
    45     if(f[0][x]+f[1][x]<=K) f[1][x]=-0x3f3f3f3f;
    46     if(f[1][x]==K) {
    47         f[1][x]=-0x3f3f3f3f; f[0][x]=0; ++ans;
    48     } 
    49     if(x==1) if(f[0][x]+f[1][x]>K) ++ans;
    50 }
    51 
    52 bool check(int x) {
    53     memset(f[0],0x3f,sizeof(f[0])); memset(f[1],-0x3f,sizeof(f[1]));
    54 //    cout<<f[1][0]<<endl;
    55     K=x; ans=0;
    56     dfs(1,0);
    57     return ans<=m; 
    58 }
    59 
    60 int main() {
    61     int x,y;
    62     n=rd(); m=rd();
    63     for(int i=1;i<=n;i++) col[i]=rd();
    64     for(int i=1;i<n;i++) {
    65         x=rd(); y=rd(); add_edge(x,y); add_edge(y,x); 
    66     }
    67     int l=0,r=n,res=0;
    68     while(l<=r) {
    69         int mid=(l+r)>>1;
    70         if(check(mid)) res=mid,r=mid-1;
    71         else l=mid+1;
    72     } 
    73     printf("%d",res);
    74     return 0;
    75 }
    View Code

    3. 树形 dp,solution 在注释里面。至于为什么 k 要正序枚举,因为一开始的时候必须加上子树全为白色情况。

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <vector>
     6 #include <cmath>
     7 #include <queue>
     8 #include <map>
     9 
    10 #define ll long long
    11 
    12 using namespace std;
    13 int rd() {
    14     int f=1,sum=0; char ch=getchar();
    15     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    16     while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    17     return sum*f;
    18 }
    19 ll lrd() {
    20     ll f=1,sum=0; char ch=getchar();
    21     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    22     while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    23     return sum*f;
    24 }
    25 /*
    26 f[x][k] 表示在x点 其中x的子树有k个黑点 sz[x]-k个白点的最大收益
    27 ans=f[1][k]
    28 考虑经过 y-x 这条边的点对数量
    29 考虑y中选v个黑点 
    30  v*(k-v)个点对
    31 白点: (sz[y]-v)*(n-k-(sz[y]-v))
    32 树上背包 
    33 */ 
    34 const int N=2002;
    35 struct edge {
    36     int nex,to,w;
    37 }e[N<<1];
    38 ll f[N][N];
    39 int head[N],sz[N],cnt,n,m;
    40 
    41 void add_edge(int x,int y,int z) {
    42     e[++cnt]=(edge){head[x],y,z}; head[x]=cnt;
    43 }
    44 
    45 void dfs(int x,int ff) {
    46     sz[x]=1; memset(f[x],-1,sizeof(f[x])); f[x][0]=0;
    47     for(int i=head[x];i;i=e[i].nex) {
    48         int y=e[i].to;
    49         if(y==ff) continue;
    50         dfs(y,x); sz[x]+=sz[y];
    51     }
    52     for(int i=head[x];i;i=e[i].nex) {
    53         int y=e[i].to;
    54         if(y==ff) continue;
    55         for(int j=min(sz[x],m);j>=0;j--) {
    56             for(int k=0;k<=min(sz[y],j);k++) {
    57                 if(f[x][j-k]<0) continue;
    58                 ll val=1ll*e[i].w*k*(m-k)+1ll*e[i].w*(sz[y]-k)*(n-m-(sz[y]-k));
    59             //    cout<<val<<endl;
    60                 f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]+val);
    61             }
    62         }
    63     }
    64 }
    65 
    66 int main() {
    67     int x,y,z;
    68     n=rd(); m=rd();
    69     for(int i=1;i<n;i++) x=rd(),y=rd(),z=rd(),add_edge(x,y,z),add_edge(y,x,z);
    70     dfs(1,0);
    71     printf("%lld",f[1][m]);
    72 }
    View Code

     4. 

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <vector>
     6 #include <cmath>
     7 #include <queue>
     8 #include <map>
     9 
    10 #define ll long long
    11 
    12 using namespace std;
    13 int rd() {
    14     int f=1,sum=0; char ch=getchar();
    15     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    16     while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    17     return sum*f;
    18 }
    19 ll lrd() {
    20     ll f=1,sum=0; char ch=getchar();
    21     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    22     while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    23     return sum*f;
    24 }
    25 
    26 /*
    27 设 f[x][j] x的子树都被覆盖 表示x点向上覆盖至少j层需要的花费
    28 那么假如在x点染黑则还需要计算向下j层花费
    29 g[x][j] 表示[dep_x,dep_x+j-1]层不一定覆盖,但 [dep_x+j,inf] 覆盖到的花费 
    30 f[x][j]=min(f[x][j]+g[y][j],f[y][j+1]+g[x][j+1])
    31 g[x][j]=sum g[y][j-1]
    32 f[x][j]=min(f[x][j+1]) g[x][j]=min(g[x][j-1])
    33 */
    34 const int N=500005;
    35 struct edge {
    36     int nex,to;
    37 }e[N<<1];
    38 ll f[22][N],g[22][N];
    39 int cost[N],col[N],head[N],cnt,n,m,D;
    40 
    41 void add_edge(int x,int y) {
    42     e[++cnt]=(edge){head[x],y}; head[x]=cnt;
    43 }
    44 
    45 void dfs(int x,int ff) {
    46     for(int i=1;i<=D;i++) f[i][x]=cost[x];
    47     if(col[x]) f[0][x]=g[0][x]=cost[x];
    48     f[D+1][x]=(1ll<<60);
    49     for(int i=head[x];i;i=e[i].nex) {
    50         int y=e[i].to; if(y==ff) continue;
    51         dfs(y,x);
    52         for(int j=0;j<=D;j++) f[j][x]=min(f[j][x]+g[j][y],f[j+1][y]+g[j+1][x]);
    53         for(int j=D;j>=0;j--) f[j][x]=min(f[j][x],f[j+1][x]);
    54         g[0][x]=f[0][x];
    55         for(int j=1;j<=D;j++) g[j][x]+=g[j-1][y];
    56         for(int j=1;j<=D;j++) g[j][x]=min(g[j][x],g[j-1][x]); 
    57     } 
    58 }
    59 
    60 int main() {
    61     int x,y;
    62     n=rd(); D=rd();
    63     for(int i=1;i<=n;i++) cost[i]=rd();
    64     m=rd();
    65     for(int i=1;i<=m;i++) x=rd(),col[x]=1;
    66     for(int i=1;i<n;i++) x=rd(),y=rd(),add_edge(x,y),add_edge(y,x);
    67     dfs(1,0);
    68     printf("%lld",g[0][1]);
    69     return 0;
    70 }
    View Code
  • 相关阅读:
    Linux 系统的启动过程
    Oracle中row_number()、rank()、dense_rank() 的区别
    Java 动态打印菱形代码之for循环的使用
    Oracle 体系结构chapter2
    Oracle 11g 概述 chaper1
    go解决ctrl+鼠标左键或F12失效问题
    解决unrecognized import path "golang.org/x/sys/windows"问题
    设计规范
    性能分析
    用IDEA导入项目时,项目中的SpringBoot注解无法识别
  • 原文地址:https://www.cnblogs.com/xugangfan/p/15130923.html
Copyright © 2020-2023  润新知