Description
对于一个无向图G 上的简单环 ( s i m p l e c y c l e ) C (simple\ cycle) C (simple cycle)C 以及 G G G 上的一条边 e e e,我们称 e e e 是C 的「弦」当且仅当 e e e 的两端点都在 C C C 上且 e e e 本身不是存在于 C C C 的边。
现在给你一个无向图以及好多个询问,每个询问会给你此图上的其中一个简单环,请回答此简单环有多少条「弦」。
Input
输入共有 1 + m + 1 + q 1+m+1+q 1+m+1+q 行。
第一行有两个正整数 n , m n,m n,m,分别代表图的点数和边数。
接下来 M M M 行中的第$ i$ 行有两个整数 x i , y i x_i,y_i xi,yi,代表此图第$ i$ 条边的两端点。
接着有一个正整数 q q q,代表有多少个询问。
最后 q q q行中的第 i i i行有 k i + 1 k_i+1 ki+1个整数,第 1 1 1个整数是 k i k_i ki 代表第$i 个 询 问 的 环 的 长 度 , 接 下 来 的 个询问的环的长度,接下来的 个询问的环的长度,接下来的k_i 个 整 数 个整数 个整数v_{i,0},v_{i,1},…,v_{i,k_i −1}$ 代表此环的的 k i k_i ki 个点,对于所有 0 ≤ j < k i 0\le j<k_i 0≤j<ki,点 v i , j v_{i,j} vi,j 和点 v i , ( j + 1 ) m o d k i v_{i,(j+1)mod\ k_i} vi,(j+1)mod ki 是此环中相邻的两个点。
( 3 ≤ n , m ≤ 3 ⋅ 1 0 5 , 1 ≤ q ≤ 1 0 5 , x i < y i , 3 ≤ k i ≤ m , ∑ 1 ≤ i ≤ q k i ≤ 3 ⋅ 1 0 5 ) (3\le n,m\le 3\sdot 10^5,1\le q\le 10^5,x_i<y_i,3\le k_i\le m,\sum\limits_{1\le i\le q}k_i\le 3\cdot 10^5) (3≤n,m≤3⋅105,1≤q≤105,xi<yi,3≤ki≤m,1≤i≤q∑ki≤3⋅105)
Output
总共要输出 Q Q Q行,每个询问个输出一行包含一个整数,代表第 i i i 个询问所给的环共有多少条「弦」。
Sample Input
7 10
0 1
1 2
2 3
3 4
4 5
5 6
0 6
0 3
3 6
2 5
4
7 0 1 2 3 4 5 6
5 0 3 2 5 6
5 0 1 2 5 6
3 0 3 6
Sample Output
3
1
0
0
Solution
对环分块,假设分块界限为 S = ⌊ m 2 ⌋ S=\lfloor\sqrt{\frac{m}{2}}\rfloor S=⌊2m⌋,直接统计两点在环上的边,去掉该环的边数即为该环的弦数
1.若 k i > S k_i>S ki>S,该类环至多 O ( m ) O(\sqrt{m}) O(m)个,直接枚举每条边判断其是否在环上即可,时间复杂度 O ( m m ) O(m\sqrt{m}) O(mm)
2.若 k i ≤ S k_i\le S ki≤S,对于每个点 u u u,记录包含该点环的编号,枚举 u u u的所有邻接点 v v v,给 v v v点打上 u u u的标记,之后依次枚举 u u u所在环上的点,看其是否有 u u u的标记即可,枚举 v v v的累加复杂度 O ( m ) O(m) O(m),这类环上每个点最多被遍历 O ( m ) O(\sqrt{m}) O(m)次,而环上点的个数和是 O ( m ) O(m) O(m)规模,时间复杂度 O ( m m ) O(m\sqrt{m}) O(mm)
注意到一条弦不要重复统计,记录 u u u在环上的编号后,寻找的是编号大于 u u u编号且被打上 u u u标记的 v v v点
同时 S S S没有取 ⌊ m ⌋ \lfloor\sqrt{m}\rfloor ⌊m⌋的原因是第二部分的常数更大,故降低环上每个点被遍历次数
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int,int>P;
const int maxn=300005,maxq=100005,S=775;
int n,m,q,x[maxn],y[maxn],ans[maxn],vis[maxn];
vector<int>g[maxn],cir[maxq];
vector<P>pos[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&x[i],&y[i]);
g[x[i]].push_back(y[i]),g[y[i]].push_back(x[i]);
}
scanf("%d",&q);
memset(vis,-1,sizeof(vis));
for(int l=0;l<q;l++)
{
int k;
scanf("%d",&k);
ans[l]=-k;
if(k>S)
{
for(int i=0;i<k;i++)
{
int temp;
scanf("%d",&temp);
vis[temp]=l;
}
for(int i=0;i<m;i++)
if(vis[x[i]]==l&&vis[y[i]]==l)ans[l]++;
}
else
{
cir[l].resize(k);
for(int i=0;i<k;i++)
{
scanf("%d",&cir[l][i]);
pos[cir[l][i]].push_back(P(l,i));
}
}
}
memset(vis,-1,sizeof(vis));
for(int u=0;u<n;u++)
{
for(int i=0;i<g[u].size();i++)vis[g[u][i]]=u;
for(int i=0;i<pos[u].size();i++)
{
int id=pos[u][i].first;
for(int j=pos[u][i].second+1;j<cir[id].size();j++)
if(vis[cir[id][j]]==u)ans[id]++;
}
}
for(int i=0;i<q;i++)printf("%d\n",ans[i]);
return 0;
}