• 【洛谷P4630】铁人两项


    题目

    题目链接:https://www.luogu.com.cn/problem/P4630
    比特镇的路网由 (m) 条双向道路连接的 (n) 个交叉路口组成。
    最近,比特镇获得了一场铁人两项锦标赛的主办权。这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程。
    比赛的路线要按照如下方法规划:

    1. 先选择三个两两互不相同的路口 (s, c)(f),分别作为比赛的起点、切换点(运动员在长跑到达这个点后,骑自行车前往终点)、终点。
    2. 选择一条从 (s) 出发,经过 (c) 最终到达 (f) 的路径。考虑到安全因素,选择的路径经过同一个点至多一次。

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

    思路

    考虑如果我们枚举了起点和终点,那么有哪些点可以作为中间点。显然中间点一定需要满足在起点到终点的任意一条简单路径上即可。
    在图上的问题不是很好做。我们发现如果一条路径经过了一个点双,那么这个点双里的点是都可以作为中间点的。所以我们考虑先 Tarjan 缩点,建出圆方树。问题转化到树上。
    现在任意两点之间有且仅有一条路径,所以我们只需要知道他们路径之间点双的大小即可。所以我们可以给每一个方点一个权值,为这个点双的大小。然后统计路径上权值之和。
    但是一个点会出现在两个点双内,所以我们可能会算重。所以我们给每一个圆点 (-1) 的权值,这样如果走了这个点就会减去相应被算重的部分。
    时间复杂度 (O(n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=200010,M=400010;
    int n,m,num;
    ll ans,sum;
    bool vis[N];
    
    struct edge
    {
    	int next,to;
    };
    
    struct Tree
    {
    	int tot,head[N];
    	ll val[N],siz[N];
    	edge e[M];
    	Tree()
    	{
    		memset(head,-1,sizeof(head));
    		memset(val,-1,sizeof(val));
    	}
    	
    	void add(int from,int to)
    	{
    		e[++tot]=(edge){head[from],to};
    		head[from]=tot;
    	}
    	
    	void dfs1(int x,int fa,ll dis)
    	{
    		vis[x]=1;
    		if (x<=n)
    			sum+=dis+val[x],siz[x]=1;
    		for (int i=head[x];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (v!=fa)
    			{
    				dfs1(v,x,dis+val[x]);
    				siz[x]+=siz[v];
    			}
    		}
    	}
    
    	void dfs2(int x,int fa)
    	{
    		if (x<=n) ans+=sum;
    		for (int i=head[x];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (v!=fa)
    			{
    				sum=sum+val[v]*(num-siz[v])-val[x]*siz[v];
    				dfs2(v,x);
    				sum=sum-val[v]*(num-siz[v])+val[x]*siz[v];
    			}
    		}
    	}
    }T;
    
    struct Tarjan
    {
    	int tot,cnt,head[N],dfn[N],low[N];
    	edge e[M];
    	stack<int> st;
    	Tarjan() { memset(head,-1,sizeof(head)); }
    	
    	void add(int from,int to)
    	{
    		e[++tot]=(edge){head[from],to};
    		head[from]=tot;
    	}
    	
    	void tarjan(int x)
    	{
    		dfn[x]=low[x]=++tot;
    		st.push(x);
    		for (int i=head[x];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (!dfn[v])
    			{
    				tarjan(v);
    				low[x]=min(low[x],low[v]);
    				if (low[v]>=dfn[x])
    				{
    					 int y,siz=1; cnt++;
    					 do {
    					 	y=st.top(); st.pop();
    					 	T.add(cnt,y); T.add(y,cnt);
    					 	siz++;
    					 } while (y!=v);
    					 T.add(cnt,x); T.add(x,cnt);
    					 T.val[cnt]=siz;
    				}
    			}
    			else low[x]=min(low[x],dfn[v]);
    		}
    	}
    }G;
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		G.add(x,y); G.add(y,x);
    	}
    	G.tot=0; G.cnt=n;
    	for (int i=1;i<=n;i++)
    		if (!G.dfn[i])
    		{
    			while (G.st.size()) G.st.pop();
    			G.tarjan(i);
    		}
    	for (int i=1;i<=n;i++)
    		if (!vis[i])
    		{
    			sum=1;
    			T.dfs1(i,0,0);
    			num=T.siz[i];
    			T.dfs2(i,0);
    		}
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    < java.util >-- Set接口
    Codeforces 627 A. XOR Equation (数学)
    Codeforces 161 B. Discounts (贪心)
    Codeforces 161 D. Distance in Tree (树dp)
    HDU 5534 Partial Tree (完全背包变形)
    HDU 5927 Auxiliary Set (dfs)
    Codeforces 27E. Number With The Given Amount Of Divisors (暴力)
    lght oj 1257
    Codeforces 219D. Choosing Capital for Treeland (树dp)
    Codeforces 479E. Riding in a Lift (dp + 前缀和优化)
  • 原文地址:https://www.cnblogs.com/stoorz/p/14275496.html
Copyright © 2020-2023  润新知