#Tarjan,貪心#LOJ 3684 「COCI 2022.3」Usmjeravanje
阿新 • • 發佈:2022-03-25
分析
可以發現題目實際上求的是最小強連通分量個數。
並且每個強連通分量必然是由最多兩段區間 \(a_l\sim a_r,b_L\sim b_R\) 組成的
只要存在一條路 \(b_R->a_l,a_r->b_L\) 那麼這個區間就能連在一起。
將所有的邊按 \(a\) 先升序,再 \(b\) 降序的順序排序,然後按順序列舉,觀察到當前邊也許會和後面的邊有交。
如果當前邊不是 \(b_i\) 連向 \(a_i\) 一定不優,反轉之後把有交的邊刪掉,可以維護 \(b\) 的最大值,如果 \(b\) 不超過最大值就證明有交。
然後定向之後跑一遍Tarjan就可以了。
程式碼
#include <cstdio> #include <cctype> #include <algorithm> using namespace std; const int N=400011; struct node{int y,next;}e[N<<1]; struct rec{int x,y,rk;}a[N]; int dfn[N],low[N],as[N],n,m,k,et,tot,v[N],st[N],Top,scc; bool ans[N]; int iut(){ int ans=0; char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=ans*10+c-48,c=getchar(); return ans; } void print(int ans){ if (ans>9) print(ans/10); putchar(ans%10+48); } int min(int a,int b){return a<b?a:b;} bool cmp(rec x,rec y){return x.x<y.x||(x.x==y.x&&x.y>y.y);} void add(int x,int y){e[++et]=(node){y,as[x]},as[x]=et;} void tarjan(int x){ dfn[x]=low[x]=++tot,v[x]=1,st[++Top]=x; for (int i=as[x];i;i=e[i].next) if (!dfn[e[i].y]){ tarjan(e[i].y); low[x]=min(low[x],low[e[i].y]); }else if (v[e[i].y]) low[x]=min(low[x],dfn[e[i].y]); if (dfn[x]==low[x]){ int y; ++scc; do{ y=st[Top--],v[y]=0; }while (y!=x); } } int main(){ n=iut(),m=iut(),k=iut(); for (int i=1;i<n;++i) add(i,i+1); for (int i=1;i<m;++i) add(i+n,i+1+n); for (int i=1;i<=k;++i) a[i]=(rec){iut(),iut(),i}; sort(a+1,a+1+k,cmp); for (int i=1,mx=0;i<=k;++i) if (mx<a[i].y) add(a[i].y+n,a[i].x),mx=a[i].y,ans[a[i].rk]=1; else add(a[i].x,a[i].y+n); for (int i=1;i<=n+m;++i) if (!dfn[i]) tarjan(i); printf("%d",scc); for (int i=1;i<=k;++i) putchar(i==1?10:32),putchar(ans[i]+48); return 0; }