[CF901C] Bipartite Segments

题目大意

给你一个有\(n\)个点的无向图,没有偶环。我们把节点标记为\(1..n\)。

你需要回答\(q\)个询问,每一个询问由一个区间[L,R](1<=L<=R<=n)组成,你需要计算出有多少个点对[x,y](L<=x<=y<=R),满足由[x,y]之间的所有点组成的子图是一个二分图。

输入数据第一行两个整数 \(n,m (1\le n\le 3\times 10^5,1\le m\le 3\times10^5)\)代表点数和边数 接下来的\(m\)行,每行两个整数,表示一条无向边,保证无自环,保证无重边

一个整数 \(q(1\le q\le3\times 10^5)\),表示询问个数

接下来q行,每行一个询问[L,R]

对于每个询问,输出一个整数,表示满足条件的点对[x,y]的个数

解析

首先想到的一定是”没有偶环“这一特殊的性质。仔细思考一下,没有偶环,也就等价于没有环套环的情况,因为如果有两个环套在一起,如果两个简单环都是奇环,那么一定会产生一个偶环(具体证明可以自行解决)。那么,原图就是一个仙人掌,并且所有环都是奇环。

接下来考虑询问。二分图的条件是没有奇环,那么满足要求的区间就不能包括任何一个环。因此,首先用Tarjan求边双连通分量,把所有环找出来,记第\(i\)个环中最小的点为\(L[i]\),最大的点为\(R[i]\),那么对于一个区间\([1,L[i]]\)中的点,以它为左端点的区间一定不能覆盖\(R[i]\)及其以后的点。所以,我们可对每个点\(i\)求出它最远能够覆盖到的点\(nxt[i]\)(线段树区间修改最小值),对于一个询问区间\([l,r]\),二分找到满足\(nxt[i]<=r\)的最右边的点,该点左边的用\(nxt\)统计答案(\(\sum nxt[i]-i\)),右边的所有子区间都满足条件,相加即为询问的答案。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
#define N 300002
using namespace std;
const int S=600001;
const int inf=1<<30;
struct SegmentTree{
    int dat,add;
}t[N*4];
int head[N],ver[N*2],nxt[N*2],l;
int n,m,q,i,tim,dfn[N],low[N],top,s[N],cnt,scc[N],L[N],R[N],a[N],sum[N];
bool cut[N*2];
int read()
{
    char c=getchar();
    int w=0;
    while(c<'0'||c>'9') c=getchar();
    while(c<='9'&&c>='0'){
        w=w*10+c-'0';
        c=getchar();
    }
    return w;
}
void insert(int x,int y)
{
    ver[l]=y;
    nxt[l]=head[x];
    head[x]=l;
    l++;
}
void Tarjan(int x,int pre)
{
    dfn[x]=low[x]=++tim;
    for(int i=head[x];i!=-1;i=nxt[i]){
        int y=ver[i];
        if(!dfn[y]){
            Tarjan(y,i);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) cut[i]=cut[i^1]=1;
        }
        else if(i!=(pre^1)) low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x)
{
    scc[x]=cnt;
    L[cnt]=min(L[cnt],x);
    R[cnt]=max(R[cnt],x);
    for(int i=head[x];i!=-1;i=nxt[i]){
        if(cut[i]) continue;
        int y=ver[i];
        if(!scc[y]) dfs(y);
    }
}
void update(int p)
{
    t[p].dat=min(t[p*2].dat,t[p*2+1].dat);
}
void spread(int p)
{
    if(t[p].add!=inf){
        t[p*2].add=min(t[p*2].add,t[p].add);
        t[p*2+1].add=min(t[p*2+1].add,t[p].add);
        t[p*2].dat=min(t[p*2].dat,t[p].add);
        t[p*2+1].dat=min(t[p*2+1].dat,t[p].add);
        t[p].add=inf;
    }
}
void build(int p,int l,int r)
{
    t[p].add=t[p].dat=inf;
    if(l==r) return;
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
}
void change(int p,int l,int r,int ql,int qr,int x)
{
    if(ql<=l&&r<=qr){
        t[p].dat=min(t[p].dat,x);
        t[p].add=min(t[p].add,x);
        return;
    }
    int mid=(l+r)/2;
    spread(p);
    if(ql<=mid) change(p*2,l,mid,ql,qr,x);
    if(qr>mid) change(p*2+1,mid+1,r,ql,qr,x);
    update(p);
}
int ask(int p,int l,int r,int x)
{
    if(l==r) return t[p].dat;
    int mid=(l+r)/2;
    spread(p);
    if(x<=mid) return ask(p*2,l,mid,x);
    return ask(p*2+1,mid+1,r,x);
}
signed main()
{
    memset(head,-1,sizeof(head));
    memset(L,0x3f,sizeof(L));
    n=read();m=read();
    for(i=1;i<=m;i++){
        int u=read(),v=read();
        insert(u,v);
        insert(v,u);
    }
    for(i=1;i<=n;i++){
        if(!dfn[i]) Tarjan(i,S);
    }
    for(i=1;i<=n;i++){
        if(!scc[i]) cnt++,dfs(i);
    }
    build(1,1,n);
    for(i=1;i<=cnt;i++){
        if(R[i]!=L[i]) change(1,1,n,1,L[i],R[i]);
    }
    for(i=1;i<=n;i++){
        a[i]=ask(1,1,n,i);
        sum[i]=sum[i-1]+a[i]-i;
    }
    q=read();
    for(i=1;i<=q;i++){
        int x=read(),y=read();
        int p=upper_bound(a+x,a+y+1,y)-a-1;
        int num1=sum[p]-sum[x-1],num2=y-p;
        printf("%lld\n",num1+num2*(num2+1)/2);
    }
    return 0;
}
上一篇:OpenStack网络参数segment


下一篇:filetype 在搜索引擎中的使用方法(2)