问题描述
操场上有n名足球队员,编号1到n。
有的队员是好友关系,队员们只愿意把球传给自己的好友。总共有m对好友关系。
何老板要大家组队玩一种传球游戏,组队的要求是:参加游戏的队员中,任意一对队员间最多能有一次传球,且队员传出去的球没有机会再次传回自己。
何老板向你提出询问:在编号为[L,R]区间中,存在多少个区间[X,Y],有L<=X,Y<=R,并且编号在区间[X,Y]中的队员满足组队的条件。
输入格式
第一行,两个整数n和m
接下来m行,每行两个整数a,b,表示编号为a,b的两个队员是好友
接下来一行,一个整数q,表示何老板共提出了q次询问
接下来q行,每行两个整数L,R,表示一次询问的区间。
输出格式
q行,每行一个整数,表示答案。
解:
恶补了一波tarjan 以前只会用tarjan求强联通分量 这次涨见识了
定义d[i]表示 i 号点最远能到达的点的编号
注意到这是一个单增的函数 容易想到利用二分查找
主要在于求环
~割点就是将点删掉图不联通
~桥就是将边删掉图不联通
~点双联通分量就是将图中存在任意删掉一个点图仍旧是联通的
~边双联通分量就是将图中存在任意删掉一个边图仍旧是联通的
此题的坑点在于 点可能是割点 处于两个环之中
所以我就会想到利用tarjan 求简单环
也就是满足条件 low[u]<=low[v] v是u的父亲 那么v就是割点 只要依次弹栈 记录最大最小
对于一个环内的最大最小的情况 (a,b) 令对于x<=a 的节点 d[x]<b 此时只需要记录一下后缀和就行了
然后找一找 第一个d[x]>R 的情况
code: 不知道为啥只有90
//
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<stack>
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define maxnn 700000
int mark[maxnn];
int cnt;
int n,m;
ll rrr[maxnn];
ll dfn[maxnn],low[maxnn],insta[maxnn];
int las[maxnn],en[maxnn],nex[maxnn],tot;
int root,vis;
void add(int a,int b) {
en[++tot]=b;
nex[tot]=las[a];
las[a]=tot;
}
#define GC getchar ()
inline int R() {
char t;
int x=0;
int f=1;
t=GC;
while(!isdigit(t)) {
if(t=='-') f=-1;
t=GC;
}
while(isdigit(t)) {
x=x*10+t-'0';
t=GC;
}
return x*f;
}
stack<int> W;
ll f[maxnn];
int scc=0;
ll sum[maxnn];
int ttt[maxnn];
int ma[maxnn],mi[maxnn];
void tarjan(int v,int fa) {
int r;
mark[v]=1;
dfn[v]=low[v]=++vis;
W.push(v);
for(int i=las[v]; i; i=nex[i]) {
int u=en[i];
int d;
if(u!=fa) {
if(!dfn[u]) {
tarjan(u,v);
low[v]=min(low[v],low[u]);
if(dfn[v]<=low[u]) {
scc++;
do {
rrr[scc]++;
mi[scc]=min(mi[scc],W.top());
ma[scc]=max(ma[scc],W.top());
d=W.top();
W.pop();
} while(d!=v);
W.push(v);
}
} else {
low[v]=min(low[v],dfn[u]);
}
}
}
}
int main() {
int x,y;
int le,re;
memset(mi,0x7f,sizeof(mi));
n=R();
m=R();
for(int i=1; i<=m; i++) {
x=R();
y=R();
add(x,y);
add(y,x);
}
for(int i=1; i<=n; i++) {
while(W.size())W.pop();
if(!mark[i]) tarjan(i,i);
f[i]=n;
}
for(int i=1; i<=scc; i++) {
if(rrr[i]>2)
f[mi[i]]=min(f[mi[i]],1LL*(ma[i]-1));
}
for(int i=1; i<=n; i++)
{
if(f[i]==i) f[i]=n;
}
for(int i=n; i>=1; i--) {
if(f[i]==0)f[i]=10000000;
if(f[i+1]==0)f[i+1]=10000000;
f[i]=min(f[i],f[i+1]);
}
for(int i=1; i<=n; i++) {
sum[i]=sum[i-1]+f[i]-i+1;
}
int q;
q=R();
while(q--) {
le=R();
re=R();
long long m=upper_bound(f+le,f+re+1,re)-f;
m--;
long long ans=0;
ans+=sum[m]-sum[le-1];
ans+=((re-m)*(re+1)-((re-m)*(re+m+1))/2);
printf("%lld
",ans);
}
}