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(' ');} 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时候才关心。