P5811 [IOI2019] 景点划分
题意
给定一个连通图,构造一个方案将其划分成 $A,B,C$ 三个部分,大小分别为 $a,b,c$,要求至少有两个部分为原图的联通子图。
Solution
先考虑如果是一颗树怎么做。不妨设 $a\le b\le c$,这样就只需要考虑 $a,b$,剩下的部分置成 $c$ 即可。容易发现 $a\le b\le \frac{n}{2}$,故考虑以重心为根节点,如果最大的子树大小大于等于 $a$ 且小于 $n-b$ 则直接把它弄成 $A$,然后暴力去跑一个大小为 $b$ 的集合就可以了。如果最大的子树大小小于 $a$ 的话就必然无解,直接特判掉。
对于图而言,可以先缩点,然后在 deg 上面考虑,转化上面的树上问题。然后发现实在太难写了,一大堆分讨以及乱七八糟的特殊情况。考虑把图变成 dfs 树,这样每个子树就是单独的连通块,似乎很好处理,于是很开心的写掉了。提交然后 subtask 4 和 subtask 5 一片红,一看全是合法方案误判成 No Solution 了。细细思考后发现有一种情况没有考虑,即如果重心的子树有到重心的祖先的返祖边,那么这种情况可以把该子树和祖先的那个子树合并成一个连通块,让它们都是 $A$ 类节点,而我没有考虑到这点直接判无解了,加个分类讨论就好了。
关于正确性:显然地如果有一颗子树有到重心的祖先节点有返祖边,那么直接把这个子树丢到 $A$ 集合中,令所有有 $A$ 类节点的子树大小总和为 $\Sigma size_i$,那么显然的有 $a\le \Sigma size_i \lt 2\times a$。又有 $n=a+b+c \ge 2\times a+b$,故剩下的节点数必然大于等于 $b$。
时间复杂度为 $\mathcal{O}(n)$,跑得飞快。
code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int res=0,flag=1;
char ch=getchar();
while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
return res*flag;
}
struct edge
{
int to,nxt;
};
int n,m,tot=1,root,son,las;
int a,b,c;
int id[]={0,1,2,3};
int ans[100010];
int head[100010],size[100010];
bool vis[100010],used[400010],flag[100010];
struct edge ed[400010];
void init(int &a,int &b,int &c)
{
if(a>b) swap(a,b),swap(id[1],id[2]);
if(a>c) swap(a,c),swap(id[1],id[3]);
if(b>c) swap(b,c),swap(id[2],id[3]);
return ;
}
void add_edge(int fr,int to)
{
ed[++tot]=(edge){to,head[fr]};
head[fr]=tot;
return ;
}
void dfs(int fr,int fa,int &num,int opt,bool type)
{
if(num==0)
return ;
ans[fr]=opt;
num--;
for(int i=head[fr];i!=0;i=ed[i].nxt)
{
int to=ed[i].to;
if(to==fa||ans[to]!=0)
continue;
if(type==false&&used[i]!=true)
continue;
if(type==true&&to==root)
continue;
dfs(to,fr,num,opt,type);
}
return ;
}
void build(int fr,int fa)
{
size[fr]=1;
vis[fr]=true;
int tmp=0;
for(int i=head[fr];i!=0;i=ed[i].nxt)
{
int to=ed[i].to;
if(vis[to]==true)
continue;
used[i]=used[i^1]=true;
build(to,fr);
size[fr]+=size[to];
if(size[tmp]<size[to])
tmp=to;
}
if(std::max(size[tmp],n-size[fr])<=n/2)
{
root=fr,las=fa;
son=tmp;
}
return ;
}
int main(int argc,const char *argv[])
{
n=read(),m=read();
a=read(),b=read(),c=read();
init(a,b,c);
for(int i=1;i<=m;i++)
{
int fr=read()+1,to=read()+1;
add_edge(fr,to);
add_edge(to,fr);
}
build(1,0);
if(std::max(size[son],n-size[root])<a)
{
dfs(las,root,a,id[1],true);
dfs(root,0,b,id[2],false);
}
else
{
if(size[son]<n-size[root])
son=las;
dfs(son,root,a,id[1],false);
dfs(root,0,b,id[2],false);
}
for(int i=1;i<=n;i++)
{
if(a==0&&ans[i]==0)
ans[i]=id[3];
else if(a!=0)
ans[i]=0;
}
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}

浙公网安备 33010602011771号