【Codeforces 1019C】Sergey's problem

构造。

题目大意:

给定一张有向图,你需要选择一些结点染黑,其他点为白色,并满足条件:

  • 黑点之间没有边相连。
  • 对于一个白点,存在一个黑点,能在走不超过 22 条边后到达这个白点。

输出一种方案。

题解:

这是一种挺妙的构造方案。

我们定义一个点有一个状态为“被控制的”或者“不被控制的”。

我们按照某种顺序枚举所有点,如果它不是被控制的,则将它染黑,并将它 $1$ 步内能到的点都标记为被控制的。

然后倒序枚举所有点,如果它是黑点,那么把和它相连的黑点变成白色。

剩下的黑点就是满足条件的方案。

为什么这样是对的呢?

考虑 $u\rightarrow v$,且 $u
$ 和 $v$ 都是黑点,那么 $u$ 必然比 $v$ 后染色,否则 $v$ 不可能是黑点。那么在倒序访问的时候,$u$ 一定先被访问到,$v$ 就会被删除。而属于 $v$ 管辖的只是走 $1$ 条边能访问到的点,而 $u$ 到 $v$ 也是一条边,即原来 $v$ 控制的点,现在变成了 $u$ 控制。那么仍然保证所有点都“被控制”。

再考虑 $u\rightarrow v\rightarrow w$,这里 $u$ 肯定会被先访问,那么 $v$ 就没有了,而 $w$ 仍然存在。这样是没有问题的。更长的链也可依此类推。

于是时间复杂度 $O(n+m)$。

Code:

#include<cstdio>
#include<bitset>
using namespace std;
const int N=1e6+5;
struct edge{
    int to,nxt;
}e[N];
int head[N],cnt,n,m;
bool vis[N];
bitset<N>bt;
inline void dfs(int u,int dep){
    vis[u]=1;
    if(dep==1)return;
    for(int i=head[u];i;i=e[i].nxt)if(!vis[e[i].to])dfs(e[i].to,dep+1);
}
int main(){
    scanf("%d%d",&n,&m);
    while(m--){
        int u,v;
        scanf("%d%d",&u,&v);
        e[++cnt]=(edge){v,head[u]},head[u]=cnt;
    }
    for(int i=1;i<=n;++i)if(!vis[i]){
        vis[i]=bt[i]=1;
        for(int j=head[i];j;j=e[j].nxt)vis[e[j].to]=1;
    }
    for(int i=n;i;--i)if(bt.test(i))for(int j=head[i];j;j=e[j].nxt)bt.reset(e[j].to);
    printf("%d\n",bt.count());
    for(int i=1;i<=n;++i)if(bt.test(i))printf("%d ",i);
    return 0;
}