问题描述
操场上有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\n",ans); } }