zoukankan      html  css  js  c++  java
  • CF1062F Upgrading Cities 拓扑排序

    题面

    给定一张有向无环图,定义重要节点x为:对于图中每一个点y,都可以从y出发到x或者从x出发到y.
    次重要节点为删除某个节点后能满足上面条件的节点(不包括重要节点),求重要节点和次重要节点一共有多少个
    (2<=n, m<= 300000)

    题解

    我们考虑这样两个性质:

    • 当我们在拓扑排序的时候,同一时刻出现在队列中的点互不可达。
    • 除去已经排序完的点和当前在队列的点,剩下的点全都是直接or间接由当前队列中的点拓展出来的。

    我们记f[i]为点i可以到达or可以到达点i的点数之和。那么题目要求的点就是满足f[i] >= n - 2的点。
    当队列里点数大于等于3的时候,由于互不可达,因此他们必然不是我们要求的点
    当队列里点数只有1的时候,由性质2可以得出,剩下的点都可以加入f[x]
    当队列里点数为2时,假设点为x和y,那么如果后续有一个点只能被x到达,那么它显然不能对y产生贡献。打个标记就行
    一个细节:
    如果x在y前面,那么我们应该用y来判x,因为当前在更新x的f值.而当x被更新完后,只能被x到达的点已经加入队列了。

    考虑如何用y来判x:
    我们定义内部点为y可达的所有点,内部边为两个端点都是内部点的边
    如果y的某个邻居入度不是1,那么说明x有可能直接or间接指向它。
    如果x不能指向它,说明这个邻居是被内部点指向了,那么我们沿着内部边反向走,一定可以走到一个(也可能是多个)点,满足这个点没有内部点连向它,不然的话就出现了环,不符合题意。那么对于这样的点,如果x不能连向它,说明它只有一个入度且入度为y(不然的话xy都不连向它,内部点也不连向它,它早入度为0进队了。),那么y会根据这个点,给x打上标记。

    #include<bits/stdc++.h>
    using namespace std;
    #define R register int
    #define AC 301000
    #define ac 601000
    
    int n, m, ans;
    int f[AC], in[AC];
    int Head[AC], Next[ac], date[ac], tot;
    int q[AC], head, tail;
    bool z[AC];
    
    struct node{
    	int f, w;
    }way[AC];	
    
    
    inline int read()
    {
    	int x = 0;char c = getchar();
    	while(c > '9' || c < '0') c = getchar();
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x;
    }
    
    
    inline void add(int f, int w){
    	date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, in[w] ++;
    
    }
    
    void pre()
    {
    	n = read(), m = read();
    	for(R i = 1; i <= m; i ++)
    	{
    		way[i].f = read(), way[i].w = read();
    		add(way[i].f, way[i].w);
    	}
    }
    
    void get(int cnt)//tmp == 2时,前者若不合法,在get中被筛出
    {
    	int x = q[head - 1], y = q[head];
    	bool flag = 0;
    	for(R i = Head[y]; i; i = Next[i])
    		if(in[date[i]] == 1) {flag = 1; break;}
    	if(flag) z[x] = 1;
    	else f[x] += n - cnt;
    }
    
    void t_sort()
    {
    	int cnt = 0;
    	head = 1, tail = 0;
    	for(R i = 1; i <= n; i ++) 
    		if(!in[i]) q[++ tail] = i, ++ cnt;
    	while(head <= tail)
    	{
    
    		int x = q[head ++];
    		int tmp = tail - head + 1 + 1;
    		if(tmp == 1) f[x] += n - cnt;
    		if(tmp == 2) get(cnt);
    		for(R i = Head[x]; i; i = Next[i])
    		{
    			int now = date[i];
    			if(!(-- in[now])) q[++ tail] = now, ++ cnt;//tmp == 2时,后者若不合法,在这里保证他的f值正确
    		}
    	}
    }
    
    void work()
    {
    	t_sort();
    	tot = 0, memset(Head, 0, (n + 2) * 4), memset(in, 0, (n + 2) * 4);
    	for(R i = 1; i <= m; i ++) add(way[i].w, way[i].f);		
    	t_sort();
    	for(R i = 1; i <= n; i ++)
    		if(!z[i] && f[i] >= n - 2) ++ ans;
    	printf("%d
    ", ans); 
    }
    
    int main()
    {
    //	freopen("in.in", "r", stdin);
    	pre();
    	work();
    	return 0;
    }
    
    本文不允许商业性使用,个人转载请注明出处! 知识共享许可协议
    本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。
  • 相关阅读:
    实现tip浮层
    c++实现冒泡排序
    一天一小段js代码(no.4)
    html5 css3实现图中结构
    html表格相关
    js跨浏览器的事件处理函数
    js实现DOM结构
    CSS3实现鼠标移动到图片上图片变大(缓慢变大,有过渡效果,放大的过程是有动画过渡的,这个过渡的时间可以自定义)
    正则表达式学习笔记
    使用JS与CSS3的翻转实现3D翻牌效果
  • 原文地址:https://www.cnblogs.com/ww3113306/p/15450008.html
Copyright © 2011-2022 走看看