【刷题】LOJ 2587 「APIO2018」铁人两项

题目描述

比特镇的路网由 (m) 条双向道路连接的 (n) 个交叉路口组成。

最近,比特镇获得了一场铁人两项锦标赛的主办权。这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程。

比赛的路线要按照如下方法规划:

1、先选择三个两两互不相同的路口 (s)(c)(f) ,分别作为比赛的起点、切换点(运动员在长跑到达这个点后,骑自行车前往终点)、终点。

2、选择一条从 (s) 出发,经过 (c) 最终到达 (f) 的路径。考虑到安全因素,选择的路径经过同一个点至多一次。

在规划路径之前,镇长想请你帮忙计算,总共有多少种不同的选取 (s)(c)(f) 的方案,使得在第 2 步中至少能设计出一条满足要求的路径。

输入格式

第一行包含两个整数 (n)(m) ,分别表示交叉路口和双向道路的数量。

接下来 (m) 行,每行两个整数 (v_i) ​​,(u_i) 。表示存在一条双向道路连接交叉路口 (v_i)(u_i) ((1 le v_i, u_i le n, v_i eq u_i))。

保证任意两个交叉路口之间,至多被一条双向道路直接连接。

输出格式

输出一行,包括一个整数,表示能满足要求的不同的选取 (s)(c)(f) 的方案数。

样例

样例输入1

4 3
1 2
2 3
3 4

样例输出1

8

样例解释1

在第一个样例中,有以下 8 种不同的选择 ((s, c, f)) 的方案:((1, 2, 3))((1, 2, 4))((1, 3, 4))((2, 3, 4))((3, 2, 1))((4, 2, 1))((4, 3, 1))((4, 3, 2))

样例输入2

4 4
1 2
2 3
3 4
4 2

样例输出2

14

样例解释2

在第二个样例中,有以下 14 种不同的选择 ((s, c, f)) 的方案:((1, 2, 3))((1, 2, 4))((1, 3, 4))((1, 4, 3))((2, 3, 4))((2, 4, 3))((3, 2, 1))((3, 2, 4))((3, 4, 1))((3, 4, 2))((4, 2, 1))((4, 2, 3))((4, 3, 1))((4, 3, 2))

数据范围与提示

子任务 1(5 分):(n le 10 , m le 100)

子任务 2(11 分):(n le 50 , m le 100)

子任务 3(8 分):(n le 100\,000) ,每个交叉路口至多作为两条双向道路的端点。

子任务 4(10 分):(n le 1\,000) ,在路网中不存在环。

存在环是指存在一个长度为 (k) ((kge 3)) 的交叉路口序列 (v_1, v_2, ldots v_k) ,序列中的路口编号两两不同,且对于 (i)(1)(k-1) ,有一条双向道路直接连接路口 (v_i)(v_{i+1}) ,且有一条双向道路直接连接路口 (v_k)(v_1)

子任务 5(13 分):(n le 100\,000) ,在路网中不存在环。

子任务 6(15 分):(n le 1\,000) ,对于每个交叉路口,至多被一个环包含。

子任务 7(20 分):(n le 100\,000) ,对于每个交叉路口,至多被一个环包含。

子任务 8(8 分):(n le 1\,000, m le 2\,000)

子任务 9(10 分):(n le 100\,000, m le 200\,000)

题解

考场上就多写了个树的,后来发现圆方树的做法好简单啊,居然没想出来qwq

求出点双并且建出圆方树后,假设确定了 (s)(f) ,那么 (c) 的情况就是 (s)(f) 的路径上的点相邻的不同的圆点的个数

令每个方点的权值等于其度数,圆点的权值等于 (-1) ,那么 (c) 的数量就等于 (s)(f) 路径上的权值和

所以就可以一个树形dp,线性统计答案了

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=100000+10,MAXM=200000+10;
int n,m,e,to[MAXM<<2],nex[MAXM<<2],out[MAXM<<2],beg[MAXN<<1],val[MAXN<<1],Visit_Num,DFN[MAXN],LOW[MAXN],cnt,Be[MAXN],size[MAXN<<1],vis[MAXN<<1],bel[MAXN<<1],all[MAXN<<1],clk;
ll ans;
std::stack<int> s;
std::vector<int> point[MAXN];
template<typename T> inline void read(T &x)
{
	T data=0,w=1;
	char ch=0;
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
	x=data*w;
}
template<typename T> inline void write(T x,char ch='')
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
	if(ch!='')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void insert(int x,int y)
{
	to[++e]=y;
	nex[e]=beg[x];
	out[e]=x;
	beg[x]=e;
}
inline void Tarjan(int x,int f)
{
	DFN[x]=LOW[x]=++Visit_Num;bel[x]=clk;all[clk]++;
	for(register int i=beg[x];i;i=nex[i])
		if(to[i]==f)continue;
		else if(!DFN[to[i]])
		{
			s.push(i);
			Tarjan(to[i],x);
			chkmin(LOW[x],LOW[to[i]]);
			if(LOW[to[i]]>=DFN[x])
			{
				int temp;++cnt;
				do{
					temp=s.top();
					s.pop();
					if(Be[out[temp]]!=cnt)
					{
						Be[out[temp]]=cnt;
						point[cnt].push_back(out[temp]);
					}
					if(Be[to[temp]]!=cnt)
					{
						Be[to[temp]]=cnt;
						point[cnt].push_back(to[temp]);
					}
				}while(out[temp]!=x||to[temp]!=to[i]);
			}
		}
		else if(DFN[to[i]]<DFN[x])s.push(i),chkmin(LOW[x],DFN[to[i]]);
}
inline void dfs(int x,int f)
{
	vis[x]=1;
	for(register int i=beg[x];i;i=nex[i])
		if(to[i]==f)continue;
		else
		{
			dfs(to[i],x);
			ans+=1ll*val[x]*size[x]*size[to[i]];
			size[x]+=size[to[i]];
		}
	if(x<=n)size[x]++;
	if(x<=n)ans+=1ll*val[x]*(size[x]-1)*(all[bel[x]]-size[x])-all[bel[x]]+1;
	else ans+=1ll*val[x]*size[x]*(all[bel[x]]-size[x]);
}
int main()
{
	read(n);read(m);
	for(register int i=1;i<=m;++i)
	{
		int u,v;read(u);read(v);
		insert(u,v);insert(v,u);
	}
	for(register int i=1;i<=n;++i)
		if(!DFN[i])clk++,Tarjan(i,0);
	e=0;memset(beg,0,sizeof(beg));
	for(register int i=1;i<=n;++i)val[i]=-1;
	for(register int i=1;i<=cnt;++i)
		for(register int j=0,lt=point[i].size();j<lt;++j)
		{
			bel[i+n]=bel[point[i][j]];
			insert(i+n,point[i][j]);
			insert(point[i][j],i+n);
			val[i+n]++;
		}
	for(register int i=1;i<=n;++i)
		if(!vis[i])dfs(i,0);
	write(ans<<1,'
');
	return 0;
}
原文地址:https://www.cnblogs.com/hongyj/p/9544520.html