CF1062F Upgrading Cities

CF1062F Upgrading Cities 

CF1062F]Upgrading Cities[拓扑排序]

 

拓扑序好题

直接处理到达集合,需要bitset

 

其实不关心那么多。

重要点:能到的和到它的sz=n-1

次重要点:如果删除一个点之后重要,那么这个点一定只有一个不互相可达点。删除就删除它。

我们只要考虑,多少点的互相可达集合sz>=n-2

 

DAG考虑进行topo

发现,任意时刻,队列中点两两不可达

如果队列长度>2这些点都不用考虑了

如果长度=1,后面的未进队点都可以到达

=2,如果另一个点b后继有一个c当前入度是1,那么必然b,c都不可以由这个点到达。不用考虑了

否则,未进队的点都可以到达。

 

同时反向topo再来一边,就处理了能到x的点的个数。

总个数>=n-2才可以。

 

其实如果不用考虑,不做特殊处理,总个数就必然<n-2了

 

简单证明正确性:

1.重要点:一定会两次topo都在长度为1的时候出现,个数=n-1

2.次重要点:唯一不互相可达的点,要么在之前出队,要么就是队列中的紧接着的一个。都考虑了。

3.无贡献点:不互相可达的点很多,必然会存在一些点加不到集合里去。

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}
namespace Modulo{
const int mod=998244353;
int ad(int x,int y){return (x+y)>=mod?x+y-mod:x+y;}
void inc(int &x,int y){x=ad(x,y);}
int mul(int x,int y){return (ll)x*y%mod;}
void inc2(int &x,int y){x=mul(x,y);}
int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
}
//using namespace Modulo;
namespace Miracle{
const int N=300000+5;
const int inf=0x3f3f3f3f;
int n,m;
int du[N];
int b[N][2];
vector<int>to[N];
int go[N];
int q[N],l,r,df;
void topo(){
    l=1,r=0;df=0;
    for(reg i=1;i<=n;++i){
        if(du[i]==0) q[++r]=i;
    }
    while(l<=r){
        int x=q[l];++df;
        int sz=r-l+1;
        ++l;
        if(sz==1){
            go[x]+=n-df;
        }else if(sz==2){
            int b=q[l];
            int lp=0;
            for(solid y:to[b]){
                if(du[y]==1) {lp=1;break;}
            }
            if(!lp){
                go[x]+=n-df-1;
            }
        }


        for(solid y:to[x]){
            --du[y];
            if(du[y]==0){
                q[++r]=y;
            }
        }
    }
}
int main(){
    rd(n);rd(m);
    for(reg i=1;i<=m;++i){
        rd(b[i][0]);rd(b[i][1]);
        to[b[i][0]].pb(b[i][1]);
        ++du[b[i][1]];
    }
    topo();
    for(reg i=1;i<=n;++i) to[i].clear(),du[i]=0;
    for(reg i=1;i<=m;++i){
        to[b[i][1]].pb(b[i][0]);  
        ++du[b[i][0]];
    }
    topo();
    int ans=0;
    for(reg i=1;i<=n;++i){
        if(go[i]>=n-2) ++ans;
    }
    ot(ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/  

关键性质:topo序队列中点两两不可达。只在队列长度<=2时候才关心。

 

posted @ 2019-05-28 15:32  *Miracle*  阅读(285)  评论(0编辑  收藏  举报