[loj3176]景点划分
不妨假设\(a\le b\le c\),并钦定连通的集合为\(A,B\)
建立dfs树,取节点\(k\)满足\(sz_{k}\ge a\)且\(\forall son,sz_{son}<a\)
删除\(k\)后,记\(k\)子树外的连通块为\(S\),取\(T=\complement_{V}S\),并分类讨论:
- 若\(|S|<a\),则\(A,B\)均需包含\(k\),无解
- 若\(a\le |S|<b\),则\(|T|\ge b\),在\(S\)和\(T\)中分别得到\(A,B\)即可
- 若\(|S|\ge b\),则不断将\(S\)中某个\(son\)的子树调整到\(T\)中直至\(|T|\ge a\)
由于\(sz_{son}<a\),即\(|T|<2a\),仍有\(|S|\ge b\),在\(T\)和\(S\)中分别得到\(A,B\)即可
模拟实现上述过程,时间复杂度为\(O(n+m)\)
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,x,y,flag,now,a[4],id[4],dfn[N],low[N],sz[N],ans[N];
vector<int>e[N],son[N];
bool cmp(int x,int y){
return a[x]<a[y];
}
void dfs(int k,int fa){
int mx=0,s=0;
dfn[k]=low[k]=++dfn[0],sz[k]=1;
for(int i:e[k])
if (i!=fa){
if (dfn[i])low[k]=min(low[k],dfn[i]);
else{
dfs(i,k),son[k].push_back(i);
sz[k]+=sz[i],low[k]=min(low[k],low[i]);
if (dfn[k]<=low[i])mx=max(mx,sz[i]);
else s+=sz[i];
}
}
s+=n-sz[k];
if (max(mx,s)<a[id[1]])flag=1;
}
void dfs_son(int k){
if (!a[now])return;
a[now]--,ans[k]=now;
for(int i:son[k])dfs_son(i);
}
void dfs_e(int k){
if ((!a[now])||(ans[k]))return;
a[now]--,ans[k]=now;
for(int i:e[k])dfs_e(i);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=3;i++){
scanf("%d",&a[i]);
id[i]=i;
}
sort(id+1,id+4,cmp);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
x++,y++;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1,0);
if (flag){
for(int i=1;i<=n;i++)printf("0 ");
return 0;
}
for(int i=1;i<=n;i++)
if (sz[i]>=a[id[1]]){
bool flag=0;
for(int j:son[i])
if (sz[j]>=a[id[1]]){flag=1;break;}
if (flag)continue;
int s=1;
for(int j:son[i])
if (dfn[i]<=low[j])s+=sz[j];
if (s>=a[id[2]]){
now=id[2],a[now]--,ans[i]=now;
for(int j:son[i])
if (dfn[i]<=low[j])dfs_son(j);
now=id[1];
for(int j:e[i])
if (dfn[i]>low[j])dfs_e(j);
break;
}
now=id[1],a[now]--,ans[i]=now;
for(int j:son[i])
if (dfn[i]<=low[j])dfs_son(j);
for(int j:son[i])
if (dfn[i]>low[j])dfs_son(j);
now=id[2];
for(int j:e[i])
if (dfn[i]>low[j])dfs_e(j);
break;
}
for(int i=1;i<=n;i++){
if (!ans[i])ans[i]=id[3];
printf("%d ",ans[i]);
}
return 0;
}