1. 程式人生 > >【CodeForces】901 C. Bipartite Segments

【CodeForces】901 C. Bipartite Segments

無向連通圖 tarjan 容易 相交 play ace 偶數 segments memset

【題目】C. Bipartite Segments

【題意】給定n個點m條邊的無向連通圖,保證不存在偶數長度的簡單環。每次詢問區間[l,r]中包含多少子區間[x,y]滿足只保留[x,y]之間的點和邊構成的圖是一個二分圖。

【算法】Tarjan縮點(找環)

【題解】如果兩個奇數長度的環相交,會得到一個偶數長度的簡單環。所以原圖是不存在偶數長度環的仙人掌(每條邊只屬於一個簡單環)。

二分圖的定義:一個圖是二分圖當且僅當不存在奇數長度的環。在當前仙人掌上,二分圖實際上要求選擇的點不存在環

也就是對於圖上已有的每個環x有最小編號點min(x)和最大編號點max(x),區間不能同時包含min(x)和max(x)。(找環可以用Tarjan縮點)

為了統計區間數量,我們預處理r[i]表示以i為區間左端點,區間右端點最遠到達r[i],初始r[min(x)]=max(x)-1,然後統計後綴最小值就可以得到r[]數組。

對於詢問的區間i∈[l,r],若i>r則ans+=r-i+1,否則ans+=r[i]-i+1。容易發現r[]數組單調遞增,所以可以二分求解轉折點。

復雜度O(n log n)。

邊編號不能為0 QAQ

技術分享圖片
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define ll long long
using
namespace std; int read(){ char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c==-)t=-1; do{s=s*10+c-0;}while(isdigit(c=getchar())); return s*t; } const int maxn=300010; int tot=1,first[maxn],low[maxn],dfn[maxn],dfsnum=0,c[maxn],n,m,mins[maxn],maxs[maxn],s[maxn],d[maxn]; ll sum[maxn],ss[maxn];
bool iscut[maxn]; struct edge{int v,from;}e[maxn*2]; void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}// void tarjan(int x,int fa){ low[x]=dfn[x]=++dfsnum; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ if(!dfn[e[i].v]){ tarjan(e[i].v,x); low[x]=min(low[x],low[e[i].v]); if(low[e[i].v]>dfn[x])iscut[i]=iscut[i^1]=1; }else low[x]=min(low[x],dfn[e[i].v]); } } void dfs(int x,int y){ c[x]=y;d[y]++; for(int i=first[x];i;i=e[i].from)if(!iscut[i]&&!c[e[i].v])dfs(e[i].v,y); } int main(){ n=read();m=read(); for(int i=1;i<=m;i++){ int u=read(),v=read(); insert(u,v);insert(v,u); } tarjan(1,0);int cnt=0; for(int i=1;i<=n;i++)if(!c[i])dfs(i,++cnt); memset(mins,0x3f,sizeof(mins)); for(int i=1;i<=n;i++){ mins[c[i]]=min(mins[c[i]],i); maxs[c[i]]=max(maxs[c[i]],i); } for(int i=1;i<=n;i++)s[i]=n; for(int i=1;i<=cnt;i++)if(d[i]>1)s[mins[i]]=maxs[i]-1; for(int i=n-1;i>=1;i--)s[i]=min(s[i],s[i+1]); for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(s[i]-i+1),ss[i]=ss[i-1]+i; int q=read(); while(q--){ int u=read(),v=read(); int x=lower_bound(s+u,s+v+1,v)-s; ll ans=0; ans+=sum[x-1]-sum[u-1]; ans+=1ll*(v-x+1)*(v+1)-(ss[v]-ss[x-1]); printf("%lld\n",ans); } return 0; }
View Code

【CodeForces】901 C. Bipartite Segments