JZOJ5919. 【NOIP2018模拟10.22】逛公园

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 表示i为左端点的区间,右端点最远可以扩展到哪一个位置。
很显然,f是递增的。
对于一组询问[l,r]就应该在区间[l,r]里面找到应该位置x,
使得 f x 1 < r f x r f_{x-1}<r 且 f_x≥r 那么在x之前的全部位置对于的右端点都是 f i f_i
而从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;
}

猜你喜欢

转载自blog.csdn.net/lijf2001/article/details/83279994