题目
1. 2种做法,一种是 $O(nk\log 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