CF901C Bipartite Segments
Link
没有偶环的图就是仙人掌,这两个条件完全等价。
而二分图是没有奇环,因此一个点集的诱导子图是二分图当且仅当这个点集的诱导子图无环。
那么我们把仙人掌上的环先抠出来,设一个环中最小的和最大的点的编号分别为\(l,r\),那么\(\forall i\in[1,l],j\in[r,n]\),\([i,j]\)这个区间的诱导子图一定有环,也就是说\(\forall i\in[1,l]\),它的右端点\(j\)必须\(<r\)。
我们把所有的条件记录下来,然后一次后缀\(\min\)求出以每个位置为左端点最右能到达的位置\(lim\)。
那么对于一次询问\([l,r]\),二分出第一个\(lim_i>r\),\([l,i)\)这一部分的\(j\)的右端点都只能到\(lim_j\),\([i,r]\)这一部分的\(j\)的右端点都只能到\(r\),预处理一下\(lim\)的前缀和即可轻松解决。
#include<cstdio>
#include<cctype>
#include<vector>
using std::vector;
using ll=long long;
const int N=300007;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int min(int a,int b){return a<b? a:b;}
int max(int a,int b){return a>b? a:b;}
vector<int>E[N];
ll sum[N];int t,top,dfn[N],low[N],stk[N],lim[N];
void tarjan(int u)
{
dfn[u]=low[u]=++t,stk[++top]=u;
for(int v:E[u])
if(!dfn[v])
{
tarjan(v),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
int t,mn=u,mx=u,c=1;
do mn=min(mn,t=stk[top--]),mx=max(mx,t),++c; while(t^v);
if(c>2) lim[mn]=min(lim[mn],mx);
}
}
else low[u]=min(low[u],dfn[v]);
}
int main()
{
int n=read(),m=read();std::fill(lim+1,lim+n+1,n+1);
for(int i=1,u,v;i<=m;++i) u=read(),v=read(),E[u].push_back(v),E[v].push_back(u);
for(int i=1;i<=n;++i) if(!dfn[i]) top=0,tarjan(i);
for(int i=n-1;i;--i) lim[i]=min(lim[i],lim[i+1]);
for(int i=1;i<=m;++i) sum[i]=sum[i-1]+lim[i];
for(int q=read(),l,r,L,R,mid;q;--q)
{
l=read(),r=read(),L=l,R=r;
while(L<=R) lim[mid=(L+R)/2]<=r? L=mid+1:R=mid-1;
printf("%I64d\n",(r-l+2ll)*(r-l+1)/2-(L-l)*(r+1ll)+sum[L-1]-sum[l-1]);
}
}