【Tarjan】HDU
阿新 • • 發佈:2018-12-09
題意:
在一張有向圖上加儘可能多的邊,使它不成為強連通圖。輸出能加的邊的數量。
題解:
首先Tarjan縮點。考慮把圖上的點分成兩塊,左邊點的數量為,右邊點的數量為。,為點的總數。
左邊所有的點互相連線,一共有條邊;右邊所有的點互相連線,一共有條邊。
左邊的點與右邊連一條有向邊,一共有條邊。
所以總共能夠構成條邊。
拆開化簡就是。要使這個值最大,就要是儘可能地小。因為,所以最小的情況就是兩者差值最大的情況。
所以我們可以找出所有入度為零或出度為零的點,找一個最小值代入為。將最後構造出來的邊數減去原來存在的邊即是答案。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll N=1e5+7; struct Edge{ ll v,nxt; Edge(ll v=0,ll nxt=0):v(v),nxt(nxt){} }e[N]; int in[N],ou[N]; ll t,n,m,u,v,cs; ll p[N],edn; void add(ll u,ll v){ e[++edn]=Edge(v,p[u]);p[u]=edn; } ll dfn[N],low[N],num[N],st[N],scc[N],sccnum,idx,top; void tarjan(ll u){ dfn[u]=low[u]=++idx; st[++top]=u; for(ll i=p[u];~i;i=e[i].nxt){ ll v=e[i].v; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(!scc[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ sccnum++; while(1){ ll x=st[top--]; num[sccnum]++; scc[x]=sccnum; if(x==u) break; } } } void init(){ memset(p,-1,sizeof(p));edn=-1; memset(in,0,sizeof(in)); memset(ou,0,sizeof(ou)); memset(num,0,sizeof(num)); memset(dfn,0,sizeof(dfn)); memset(scc,0,sizeof(scc)); sccnum=idx=top=0; } void rebuild(){ for(int x=1;x<=n;x++){ int u=scc[x]; for(int i=p[x];~i;i=e[i].nxt){ int v=scc[e[i].v]; if(u==v) continue; ou[u]++,in[v]++; } } } int main(){ scanf("%lld",&t); while(t--){ init(); scanf("%lld%lld",&n,&m); for(ll i=1;i<=m;i++){ scanf("%lld%lld",&u,&v); add(u,v); } for(ll i=1;i<=n;i++) if(!scc[i]) tarjan(i); if(sccnum==1) printf("Case %lld: -1\n",++cs); else{ rebuild(); memset(dp,0,sizeof(dp)); ll tmp=n; for(ll i=1;i<=sccnum;i++){ if(in[i]==0||ou[i]==0){ tmp=min(tmp,num[i]); } } ll ans=n*n-n-tmp*(n-tmp); printf("Case %lld: %lld\n",++cs,ans-m); } } }