题目
n条线段 [ l , r ] [l,r] [l,r],有m组询问 [ x , y ] [x,y] [x,y],求有多少个 [ x , y ] [x,y] [x,y]的子区间(含 [ x , y ] [x,y] [x,y])不覆盖任意一条线段。
题解
顺便复习了下tarjan求点双。
审题!
去重貌似是很麻烦的。
用比较简单的方法。
突破口:固定住左端点,扩右端点,直到不能扩为止。通过这样来计算。
设
m
x
[
i
]
mx[i]
mx[i]表示以i为左端点,右端点最多能到哪。
显然,
m
x
[
i
]
mx[i]
mx[i]是递增的。
所以,在
[
x
,
y
]
[x,y]
[x,y]中二分出一个点z,
i
∈
[
z
,
y
]
i∈[z,y]
i∈[z,y],mx[i]>y
区间
[
z
,
y
]
[z,y]
[z,y]的所有子区间都是合法的。而
i
∈
[
x
,
z
)
i∈[x,z)
i∈[x,z),对答案的贡献为
m
x
[
i
]
−
i
+
1
mx[i]-i+1
mx[i]−i+1,用树状数组维护个前缀和就好了。
小结
①第一突破口,固定住左端点,扩右端点,直到不能扩为止。
②第二突破口,
m
x
[
i
]
mx[i]
mx[i]是递增的。
③位置i对答案的贡献为关于i的函数
c
o
n
t
r
i
b
u
t
e
(
i
)
contribute(i)
contribute(i)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 600010
#define LL long long
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct Graph{
int tot,head[N],next[N<<1],to[N<<1];
void lb(int x,int y){to[++tot]=y;next[tot]=head[x];head[x]=tot;}
}G;
int i,j,k,l1,r1,n,m,q,cs;
int Mx,Mn,u,v,w,wz,mid;
int r[N];
int opl,opr,opx,opz,TOT;
int dfn[N],low[N],st[N],T,top;
bool Bz[N];
LL tr[N];
LL ans;
int read(){
int fh=0,rs=0;char ch=0;
while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
if(ch=='-')fh=1,ch=getchar();
while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
return fh?-rs:rs;
}
void write(int x){
if(x>9)write(x/10);
P(x%10+'0');
}
int Min(int x,int y){return x<y?x:y;}
int Max(int x,int y){return x>y?x:y;}
void tarjan(int x){
int i,c0;
Bz[x]=1;
st[++top]=x;
dfn[x]=low[x]=++T;
for(i=G.head[x];i;i=G.next[i])
if(!dfn[G.to[i]]){
tarjan(G.to[i]);
low[x]=Min(low[x],low[G.to[i]]);
if(low[G.to[i]]>=dfn[x]){
Mn=Mx=x;
c0=0;
while(st[top+1]^G.to[i]){
c0++;
Mn=Min(Mn,st[top]);
Mx=Max(Mx,st[top]);
top--;
}
if(c0>1){
r[Mn]=Min(r[Mn],Mx-1);
}
}
}else low[x]=Min(low[x],dfn[G.to[i]]);
}
int lowbit(int x){return x&(-x);}
void add(int x,LL z){
for(;x<=n;x=x+lowbit(x))tr[x]=tr[x]+z;
}
LL qry(int x){
LL rs=0;
for(;x;x=x-lowbit(x))rs=rs+tr[x];
return rs;
}
LL calc(LL x){return x*(x+1)>>1;}
int main(){
n=read();m=read();
fo(i,1,n)r[i]=n;
r[n+1]=n+1;
fo(i,1,m){
u=read(),v=read();
G.lb(u,v);
G.lb(v,u);
}
fo(i,1,n)if(!Bz[i]){
tarjan(i);
}
fd(i,n-1,1)r[i]=Min(r[i+1],r[i]);
fo(i,1,n)add(i,r[i]-i+1);
q=read();
fo(cs,1,q){
u=read(),v=read();
l1=u,r1=v+1;w=r1;
while(l1<=r1){
mid=(l1+r1)>>1;
if(r[mid]>v)w=mid,r1=mid-1;else l1=mid+1;
}
ans=qry(w-1)-qry(u-1)+calc(v-w+1);
printf("%lld\n",ans);
}
return 0;
}