Sergey's problem CF1019C

这真是有趣的题目啊(不是吗?)

题目描述

今天是T一岁的生日(真不容易),当她刚出生一秒的时候,她的太……太爷爷L给了她一颗钻石(有一个编号),
当她出生2秒时,L高兴她生来畸形,却能活过2秒,给了她一个钻石项链(均有编号),在她出生3秒时,
xay兴奋过度,留给她一根纯钻石的绳子……
当她出生4秒时,L实在太开心了,死了,当他知道自己的死期时,特意为写了一张字条给她母亲(打算在T周岁时给他)
Today,T的母亲温柔和蔼地叫醒了她,T撑着她那书包一样大的脑袋,睁开了她那漂亮的眼睛,
晶莹,剔透,无与伦比,这真是这个世界上最漂亮的眼睛,可惜只有用探测隧道显微镜才能看到……
她的首富爸爸希望她能做一个好孩子,志不能残,要好好学习信奥,于是给了她……一颗有根树??(这比L的礼物差远了,不是吗》),
但是,T不想当农民(显然他理解错了),她要了一个玩具(好奇怪的要求)作为礼物,首富爸爸满足了她

由于T非常喜欢嬉戏,特别是搓炉石传说(没有什么关系),她的玩具是一个有向图Q的拼装,她不是很理解这个怎么玩~于是就有了自己的想法,她想找到一组顶点,始这组点没有两个定点 x , y ∈ Q x, y∈Q x,yQ,且对于任何一个不属于S集合的一个点中, x x x到达 y y y的距离能<=2,且x和y在原图中没有边相连;

T很小,但她自己只会胡乱搭建一个有向图(首富买的就是不一样,买的玩具有n个点,m个边( n , m &lt; = 1 0 6 ) n,m&lt;=10^6) nm<=106),找到这先点集的点就交给你了~

题目保证有解~~~~

输入格式

第一行,两个正整数, n n n m m m,表示这个题有 n n n个点, m m m条边

剩下有 m m m

每行有两个整数 x x x, y y y。表示x到y有一条有向连边

输出格式

第一行,一个整数,表示这个点集有 S S S大小

剩下 S S S

分别是这些点的编号(从小到大排序,中间有空格)

样例数据

input1
5 4
1 2
2 3
2 4
2 5
output1
4
1 3 4 5

(良心出题人

input2
3 3
1 2
2 3
3 1
output2
1
3

很不错,毕竟这是我自己翻译,比较皮~~

大意网上有一个翻译很淳朴,很清楚

题目大意

给你一个n个点,m条边的 有向图

要求选出一个点集S,满足S中的任意两点间没有边相连
且对于任何一个不属于S的点y
必然存在S中的某个点x满足

网上有一个题解写的非常详细,但很无奈,写的自我感觉并 不是很让初学者能看懂 ~
送上一个必定让人看懂 的(看不懂,私信)

先看看样例解析:

对于第一个样例:
顶点1,3,4,5未连接。 顶点2可以通过一个边缘从顶点1到达。
在这里插入图片描述

对于第二个样例
3可以在一次移动中到达顶点1,在两次移动中到达顶点2。
在这里插入图片描述

来个简单的题解

先一个图,先扫一遍 选出一个没有被删掉的点(这个删掉不是真的要删掉)
然后把它所连上的那些边(是可能有多个的)删除掉 并把这个点放入集合S中 也删除它 一直做下去,直到这一个图没有点了~
然后这个图,还原再按编号倒序遍历集合中的点,如果它指向了一个编号较小的,且在集合中的点,那么把那个点从集合中删除。
画个图举个例子:
8 7
1 2
2 3
2 4
3 4
3 5
4 5
4 6
对于这个图
我们先找到第一个点1删除其连边1 2 选择3,删除4 5;选择6,不能删除,7,8同理; 这样就把最短路1 的情况处理完了。
集合里的是1,3,6,7,8
再按编号倒序遍历集合中的点,如果它指向了一个编号较小的,且在集合中的点,那么把那个点从集合中删除,显然这个数据不太好,没有需要删除的
所以说个的答案就是,1 3 6 7 8(其实就是左边扫一遍,右边扫一遍)

在这里插入图片描述

但是不要以为可以不用右扫哦,你要想到,样例2良心的把需要右扫一遍的,不然会是1,3
显然应该是要3的,1 是被3用连上了的,所以特别要注意环这种情况

证明来源于那位大神:
因为两个属于答案集合点不能一步到达。
满足x∈V,x∉v 的节点 x,在左边扫的时候就被删掉了~
满足 x∈v,x∉S(答案集合)的节点 x,会在右边扫描的时候删除掉。显然有一个能一步到达 x 的节点 y 在集合 v 中。
如果 y∉S,有一步到 y 的节点的话, x 可以两步到达。y∈S ,x 可以由 y 一步到达。//证明其实可能不正确,但是:
最重要的关键证明:根据cf的AC可以判断,是对的( 不然其它白扯 ~
在这里插入图片描述

贴上我的代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1000005;
int n,m,cnt=0,len;
int vis[N],ans[N],last[N];
struct ss
{
    int to,next;
}e[N];
void insert(int x,int y)
{
    e[++len].next=last[x],e[len].to=y,last[x]=len;
}
int main()
{
	freopen("T.in","r",stdin);
	freopen("T.out","w",stdout); 
	int i,j;
    scanf("%d%d",&n,&m);
    for(i=0;++i<=m;)
	{
        int x,y;
        scanf("%d%d",&x,&y);
        insert(x,y); 
    }
    for(i=0;++i<=n;)//左边扫 
        if(!vis[i])
		{
            vis[i]=-2;
            for(j=last[i];j;j=e[j].next)
                vis[e[j].to]=min(vis[e[j].to],-1);
        }
    for(i=n;i>=1;--i)//右边扫 
        if(vis[i]==-2)
		{
            ans[++cnt]=i;
            for(j=last[i];j;j=e[j].next)
                vis[e[j].to]=-1;
        }
    printf("%d\n",cnt);
    for(i=cnt;i>=2;--i)
        printf("%d ",ans[i]);
    printf("%d",ans[1]);
    return 0;
}

在这里插入图片描述

谢谢观赏哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值