题意
- 给出区间[l,r],保留区间内的点和点相关联的边。
- 求子区间[x,y]的数量,满足这个区间的内的点和点组成的边是二分图。
题解
- 二分图的条件:不含奇圈。题目已经保证不含偶圈,所以要构成二分图,只要不含圈即可。
- 而且题目隐含不含圈套圈,否则就会产生偶圈。
- 我们先找到对于点x,找出s[x],表示最大的编号,不含圈,也就是[x,s[x]]区间不含圈。
- 首先我们要找到圈才行,那就Tarjan一下,找连通分量,然后记录连通分量的最小编号mn[]和最大编号mx[]。
- 那么s[mn[i]] = mx[i] - 1。这样区间[mn[i],mx[i]-1]就不会产生圈了。
- 然后就是求s[i],s数组从后往前遍历,s[i] = min(s[i],s[i+1])。(自己想想)
- 最后加入给定区间[l,r],那么我们找一个点p,满足[l,p)区间s[i]<=r,[p,r]区间满足s[i]>=r。那么最后答案就是
- 预处理前一项的前缀和。
代码
#include <bits/stdc++.h>
using namespace std;
int const N = 300000 + 10;
typedef long long ll;
int n,m,q;
int dfn[N],lowlink[N],cnt,scc,sccno[N];
int mx[N],mn[N],s[N],sz[N];
ll sum[N];
vector<int>G[N];
stack<int>st;
void Tarjan(int u,int fa){
dfn[u] = lowlink[u] = ++cnt;
st.push(u);
for(int i=0;i<G[u].size();i++){ //无向图的连通分量
int v = G[u][i];
if(v == fa) continue;
if(!dfn[v]){
Tarjan(v,u);
lowlink[u] = min(lowlink[u],lowlink[v]);
}else if(!sccno[v])
lowlink[u] = min(lowlink[u],dfn[v]);
}
if(dfn[u] == lowlink[u]){
scc++;
while(1){
sz[scc]++;
int x = st.top(); st.pop();
sccno[x] = scc;
if(x == u) break;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i,0);
for(int i=1;i<=n;i++) mx[sccno[i]] = i;
for(int i=n;i>=1;i--) mn[sccno[i]] = i;
for(int i=1;i<=n;i++) s[i] = n;
for(int i=1;i<=scc;i++) if(sz[i] > 1) s[mn[i]] = mx[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;
scanf("%d",&q);
for(int i=1;i<=q;i++){
int l,r;
scanf("%d%d",&l,&r);
int p = lower_bound(s+l,s+1+r,r) - s;
printf("%lld\n",1ll*(r-p+1)*(r-p+2)/2+sum[p-1]-sum[l-1]);
}
return 0;
}