Codeforces

Luogu

分析

「图中没有偶环」等价于「图是仙人掌」。因此本题中「一个诱导子图是二分图」等价于「一个诱导子图没有环」。

考虑求出一个 $nxt_i$ 表示以 $i$ 为左端点的最大右端点。我们找到所有的环,假设某个环上点编号的最小值为 $mn$,最大值为 $mx$,那么 $[1,mn]$ 中的所有点的 $nxt$ 都不会 $\geq mx$,即用 $mx-1$ 对 $nxt_{1..mn}$ 取 $\min$。可以用线段树实现,也可以仅在 $mn$ 处修改最后再扫一遍求出所有 $nxt$。

考虑询问怎么做。我们二分出一个最大的满足 $nxt_x\leq r$ 的位置 $x$。显然 $[x+1,r]$ 的所有子区间都合法,而 $[l,x]$ 的合法方案数等于所有 $nxt$ 的和减去一个等差数列的和,从而不难算出答案。

代码

// ===================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// ===================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#define re register
using namespace std;
typedef long long ll;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=300000+10,M=300000+10;

int n,m,q;
int nxt[N]; ll sum[N];

struct edge { int v,nxt; } e[M<<1];
int head[N];
inline void addEdge(int u,int v) {
    static int c=0;
    e[++c]=(edge){v,head[u]},head[u]=c;
}

int vis[N],in[N],sta[N],top=0;
inline void dfs(int u,int fa) {
    vis[u]=1,sta[++top]=u;
    for (re int i=head[u];i;i=e[i].nxt) {
        int v=e[i].v; if (v==fa) continue;
        if (!vis[v]) dfs(v,u);
        else if (!in[v]) {
            int mx=0,mn=n+1;
            while ("longge ak ioi") {
                int x=sta[top--]; in[x]=1;
                mx=max(mx,x),mn=min(mn,x);
                if (x==v) break;
            }
            nxt[mn]=min(nxt[mn],mx-1);
        }
    }
    if (!in[u]) --top;
}

int main() {
    n=read(),m=read();
    for (re int i=1;i<=m;++i) {
        int u=read(),v=read();
        addEdge(u,v),addEdge(v,u);
    }
    for (re int i=1;i<=n;++i) nxt[i]=n;
    for (re int i=1;i<=n;++i) if (!vis[i]) dfs(i,0);
    for (re int i=n-1;i;--i) nxt[i]=min(nxt[i+1],nxt[i]);
    for (re int i=1;i<=n;++i) sum[i]=sum[i-1]+nxt[i];
    q=read();
    while (q--) {
        int l=read(),r=read();
        int L=l,R=r;
        while (L<R) {
            int mid=(L+R+1)>>1;
            if (nxt[mid]<r) L=mid;
            else R=mid-1;
        }
        if (nxt[L]>r) --L;
        printf("%lld\n",1ll*(r-L)*(r-L+1)/2+sum[L]-sum[l-1]-1ll*(l+L-2)*(L-l+1)/2);
    }
    return 0;
}
最后修改:2021 年 03 月 24 日 02 : 58 PM