Description
小 B 带着 GF 去逛公园,公园一共有 n 个景点,标号为 1 . . . n。景点之间有 m 条路径相连。
小 B 想选择编号在一段区间 [l, r] 内的景点来游玩,但是如果这些景点的诱导子图形成了环,那么 GF 将会不高兴。
小 B 给出很多个询问 [x, y],想让你求有多少个区间 [l, r] 满足 x ≤ l, r ≤ y 且不会使 GF不高兴。
Input
第一行为两个整数 n, m,表示景点和路径的数量。
第 2 . . . m + 1 行每行两个整数 ui, vi 表示第 i 路径的两端。
第 m + 2 行是一个整数 q 表示询问的个数,接下来 m 行每行两个整数 xi, yi 表示询问。
Output
q 行,每行一个整数表示答案。
Data Constraint
对于 30% 的数据,n, m ≤ 100。
对于另外 10% 的数据,n = m + 1。
对于另外 10% 的数据,n = m
对于 100% 的数据,n, m ≤ 3 × 10^5, xi ≤ yi,不存在重边、自环,不存在一条边同时存在于两个不同的简单环。
题解
根据数据范围的最后,可以知道给出的是一棵仙人掌。
考虑一个简单环的影响,
已知这个简单环的编号最小值和最大值,
那么选择区间[l,r]的时候就一定不能跨过这个最小最大值的区间。
那么设
f
i
f_i
fi表示i为左端点的区间,右端点最远可以扩展到哪一个位置。
很显然,f是递增的。
对于一组询问[l,r]就应该在区间[l,r]里面找到应该位置x,
使得
f
x
−
1
<
r
且
f
x
≥
r
f_{x-1}<r 且 f_x≥r
fx−1<r且fx≥r那么在x之前的全部位置对于的右端点都是
f
i
f_i
fi,
而从x这个位置开始往后,所以的右端点都是r。
code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 3000003
#define M 103
#define db double
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
n=0;
ch=G();
while((ch<'0' || ch>'9') && ch!='-')ch=G();
int w=1;
if(ch=='-')w=-1,ch=G();
while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
n*=w;
}
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}
int nxt[N*2],to[N*2],lst[N],tot,x,y,z[N],top;
int n,m,q,l,r,mid,f[N];
bool bz[N],is[N];
ll ans,sum[N],S[N];
void ins(int x,int y)
{
nxt[++tot]=lst[x];
to[tot]=y;
lst[x]=tot;
}
void dfs(int x,int fa)
{
bz[z[++top]=x]=0;
is[x]=1;
for(int i=lst[x];i;i=nxt[i])
{
if(to[i]==fa)continue;
if(bz[to[i]])dfs(to[i],x);else
{
if(!is[to[i]])continue;
int mi=to[i],mx=to[i];
for(int j=top;z[j]^to[i];j--)mx=max(z[j],mx),mi=min(z[j],mi);
f[mi]=min(f[mi],mx-1);
}
}
top--;is[x]=0;
}
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
memset(f,127,sizeof(f));
memset(bz,1,sizeof(bz));
read(n);read(m);
for(int i=1;i<=m;i++)
read(x),read(y),ins(x,y),ins(y,x);
dfs(1,0);
for(int i=n;i;i--)f[i]=min(f[i],f[i+1]);
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+f[i]-i+1,S[i]=S[i-1]+i;
for(read(q);q;q--)
{
read(x);read(y);
for(l=x,r=y;l<r;)
{
mid=(l+r)>>1;
if(f[mid]<y)l=mid+1;else r=mid;
}
ans=sum[l-1]-sum[x-1]+S[y-l+1];
write(ans);P('\n');
}
return 0;
}