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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值