Newcoder 83 E.无向图的弦(分块)

186 篇文章 0 订阅

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 &lt; k i 0\le j&lt;k_i 0j<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 &lt; 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&lt;y_i,3\le k_i\le m,\sum\limits_{1\le i\le q}k_i\le 3\cdot 10^5) (3n,m3105,1q105,xi<yi,3kim,1iqki3105)

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 &gt; S k_i&gt;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 kiS,对于每个点 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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值