1. 程式人生 > >JZOJ 5919. 【NOIP2018模擬10.22】逛公園

JZOJ 5919. 【NOIP2018模擬10.22】逛公園

題目

n條線段 [ l , r ] [l,r] ,有m組詢問 [ x

, y ] [x,y] ,求有多少個 [ x , y ]
[x,y]
的子區間(含 [ x , y ] [x,y] )不覆蓋任意一條線段。

題解

順便複習了下tarjan求點雙。
審題!
去重貌似是很麻煩的。
用比較簡單的方法。
突破口:固定住左端點,擴右端點,直到不能擴為止。通過這樣來計算。
m x [ i ] mx[i] 表示以i為左端點,右端點最多能到哪。
顯然, m x [ i ] mx[i] 是遞增的。
所以,在 [ x , y ] [x,y] 中二分出一個點z, i [ z , y ] i∈[z,y] ,mx[i]>y
區間 [ z , y ] [z,y] 的所有子區間都是合法的。而 i [ x , z ) i∈[x,z) ,對答案的貢獻為 m x [ i ] i + 1 mx[i]-i+1 ,用樹狀陣列維護個字首和就好了。

小結

①第一突破口,固定住左端點,擴右端點,直到不能擴為止。
②第二突破口, m x [ i ] mx[i] 是遞增的。
③位置i對答案的貢獻為關於i的函式 c o n t r i b u t e ( 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;
}