zoukankan      html  css  js  c++  java
  • LOJ#2587[APIO2018]Duathlon铁人两项(圆方树)

    题目链接

    洛谷

    LOJ

    前置知识

    圆方树

    解析

    考虑一条从(s)(f)的路径产生的贡献是除(s, f)外经过的点数

    如果这条路径经过了某个点双连通分量,点双上的每个点都会产生贡献

    点双( ightarrow)圆方树

    建出圆方树,方点权值为代表的点双的大小

    这样两点间的路径对应成两个圆点间的路径,由于一条路径会经过(s)(f)自己所在的点双,而(c)不能在(s)(f),所以每个圆点权值为(-1)

    于是一对(s, f)的贡献就是圆方树上的路径上的点权和

    考虑每个点被多少条路径经过,乘上点权就是这个点对答案的贡献

    于是就变成了一个简单的树上路径统计问题

    因为交换(s)(f)算不同的方案,最后答案乘(2)

    代码

    自带大常数……洛谷(1000ms)(LOJ 5600ms)……

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #define MAXN 100005
    #define MAXM 200005
    
    typedef long long LL;
    struct Graph {
    	struct Edge {
    		int v, next;
    		Edge(int _v = 0, int _n = 0):v(_v), next(_n) {}
    	} edge[MAXM << 1];
    	int head[MAXN << 1], cnt;
    	void init() { memset(head, -1, sizeof head); cnt = 0; }
    	void add_edge(int u, int v) { edge[cnt] = Edge(v, head[u]); head[u] = cnt++; }
    	void insert(int u, int v) { add_edge(u, v); add_edge(v, u); }
    };
    
    void Tarjan(int, int);
    void dfs(int, int);
    
    int N, M, dfn[MAXN], low[MAXN], idx, tot;
    int stk[MAXN], top;
    int size[MAXN << 1], w[MAXN << 1], root[MAXN << 1];
    bool vis[MAXN << 1];
    LL ans;
    Graph G, T;
    
    int main() {
    	scanf("%d%d", &N, &M);
    	tot = N;
    	G.init(); T.init();
    	for (int i = 0; i < M; ++i) {
    		int u, v; scanf("%d%d", &u, &v);
    		G.insert(u, v);
    	}
    	for (int i = 1; i <= N; ++i) {
    		w[i] = -1, size[i] = 1;
    		if (!dfn[i]) Tarjan(i, 0);
    	}
    	for (int i = 1; i <= tot; ++i)
    		if (!vis[i]) {
    			root[i] = i;
    			dfs(i, 0);
    		}
    	for (int i = 1; i <= tot; ++i) ans += (LL)(size[root[i]] - size[i]) * size[i] * w[i];
    	printf("%lld
    ", ans << 1);
    
    	return 0;
    }
    void Tarjan(int u, int fa) {
    	dfn[u] = low[u] = ++idx;
    	for (int i = G.head[u]; ~i; i = G.edge[i].next) {
    		int v = G.edge[i].v;
    		if (v == fa) continue;
    		if (!dfn[v]) {
    			stk[top++] = v;
    			Tarjan(v, u);
    			low[u] = std::min(low[u], low[v]);
    			if (low[v] >= dfn[u]) {
    				int p; ++tot;
    				do {
    					p = stk[--top];
    					T.insert(tot, p);
    					++w[tot];
    				} while(p ^ v);
    				T.insert(tot, u);
    				++w[tot];
    			}
    		} else low[u] = std::min(low[u], dfn[v]);
    	}
    }
    void dfs(int u, int fa) {
    	vis[u] = 1;
    	for (int i = T.head[u]; ~i; i = T.edge[i].next) {
    		int v = T.edge[i].v;
    		if (v == fa) continue;
    		root[v] = root[u], dfs(v, u);
    		ans += (LL)size[v] * size[u] * w[u];
    		size[u] += size[v];
    	}
    }
    //Rhein_E
    
  • 相关阅读:
    动态规划精讲(一)LC最长公共子序列
    0-1背包
    折线分割平面
    母牛的故事
    数塔
    70. 爬楼梯
    文件修改的两种方式
    文件处理练习
    文件处理
    购物车理解
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10613321.html
Copyright © 2011-2022 走看看