[APIO2018]铁人两项

题目

题意:在一个无向图里面选三个点\(s\),\(c\),\(f\)

需要能够从\(s\)出发,经过\(c\),到达\(f\)点,中间不能提前经过\(f\),且需要是一个简单路径

Solution:

简单路径当然就是园方树了,想想怎么统计答案
yy一下可以发现,有一条路径\(s\)\(f\),中间能选的点就是路径上的圆点和
因为在一个点双连通分量里面,一定有一个不重复的路径到出点去。
想想怎么赋权,方点赋连接的圆点个数,圆点为-1,最后一条路径的权值和刚好就是去掉两个头的圆点个数,
然后就是个简单的dp计数了

code:

#include<bits/stdc++.h>
#define ll long long
#define R register
#define pb push_back
template<class T>
void rea(T &x)
{
	int f(0);char ch=getchar();x = 0;
	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	x = f?-x:x;
}
using namespace std;
const int N = 500005;
vector<int> I[N], e[N<<1];
ll ans;
int n, m, node, num;
int dfn[N], low[N], dfc, sta[N], tp;
int val[N<<1], siz[N<<1];
bool vis[N<<1];
void tarjan(int x)
{
	dfn[x] = low[x] = ++dfc;
	sta[++tp] = x;
	for(R int i = 0; i < I[x].size(); ++i)
	{
		if(!dfn[I[x][i]])
		{
			tarjan(I[x][i]);
			low[x] = min(low[x], low[I[x][i]]);
			if(low[I[x][i]] == dfn[x])
			{
				val[++node] = 1;
				for(R int o = 0; o != I[x][i]; --tp)
				{
					o = sta[tp];
					e[node].pb(o);
					e[o].pb(node);
					++val[node];
				}
				e[x].pb(node), e[node].pb(x);
			}
		}
		else low[x] = min(low[x], dfn[I[x][i]]);
	}
}
void cal(int x, int fa)
{
	siz[x] = (x<=n);
	//cout<<x<<" "<<val[x]<<endl;
	for(R int i = 0; i < e[x].size(); ++i) if(e[x][i] != fa)
	{
		cal(e[x][i], x);
		ans += 2ll * siz[x] * siz[e[x][i]] * val[x];
		siz[x] += siz[e[x][i]];
	}
	ans += 2ll * siz[x] * (num-siz[x]) * val[x];
}
int main()
{
	rea(n), rea(m); node = n;
	memset(val, -1, sizeof val);
	int x, y;
	for(R int i = 1; i <= m; ++i)
		rea(x), rea(y), I[x].pb(y), I[y].pb(x);//读入用I!! 
	for(R int i = 1; i <= n; ++i) 
		if(!dfn[i]) num = dfc, tarjan(i), num = dfc-num, cal(i, 0);
	printf("%lld\n", ans);
    return 0;
}

posted @ 2020-03-12 19:24  Chopsticks  阅读(139)  评论(0编辑  收藏  举报