• JZOJ3225[BJOI2013]load(圆方树+树链剖分/树上差分)


    题目链接

    JZOJ3225

    题目大意

    给出一张(N)个点(M)条边的无向图,和(Q)对点对(p_i, q_i),问最后图中每个点必定被(p_i)(q_i)的路径覆盖多少次

    (N le 1e5, M, Q le 2e5)

    样例输入 样例输出
    4 4 2
    1 2
    1 3
    2 3
    1 4
    4 2
    4 3
    2
    1
    1
    2

    解析

    考虑(p)(q)的路径必定经过哪些点,显然是(p)(q)的简单路径经过的割点

    回忆圆方树的构造,树上度数大于(1)的圆点就是原图上的割点

    那么问题就简单了,建出圆方树,然后(p)(q)路径上的圆点答案都会加(1)(因为题目说起点和终点也算,就不用特殊处理了)

    我拿了个树剖来维护,也可以树上差分

    代码

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <vector>
    #define MAXN 100005
    
    typedef long long LL;
    struct Graph {
    	struct Edge {
    		int v, next;
    		Edge(int _v = 0, int _n = 0):v(_v), next(_n) {}
    	} edge[MAXN << 2];
    	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); }
    };
    struct SegmentTree {
    	int add[MAXN << 3];
    	void update(int, int, int, int, int);
    	int query(int, int, int, int);
    };
    
    char gc();
    int read();
    void Tarjan(int, int);
    void rebuild();
    void dfs1(int);
    void dfs2(int);
    
    int N, M, Q, idx, tot;
    int dfn[MAXN << 1], top[MAXN << 1], fa[MAXN << 1], dep[MAXN << 1], low[MAXN], size[MAXN << 1], heavy[MAXN << 1];
    int stk[MAXN], stop;
    std::vector<int> bel[MAXN];
    Graph G;
    SegmentTree sgt;
    
    int main() {
    	G.init();
    	tot = N = read(), M = read(), Q = read();
    	for (int i = 1; i <= M; ++i) G.insert(read(), read());
    	Tarjan(1, 0);
    	rebuild();
    	dfs1(1);
    	top[1] = 1, idx = 0;
    	dfs2(1);
    	while (Q--) {
    		int p = read(), q = read();
    		while (top[p] ^ top[q]) {
    			if (dep[top[p]] < dep[top[q]]) std::swap(p, q);
    			sgt.update(1, 1, tot, dfn[top[p]], dfn[p]);
    			p = fa[top[p]];
    		}
    		if (dep[p] > dep[q]) std::swap(p, q);
    		sgt.update(1, 1, tot, dfn[p], dfn[q]);
    	}
    	for (int i = 1; i <= N; ++i)
    		printf("%d
    ", sgt.query(1, 1, tot, dfn[i]));
    
    	return 0;
    }
    inline char gc() {
    	static char buf[1000000], *p1, *p2;
    	if (p1 == p2) p1 = (p2 = buf) + fread(buf, 1, 1000000, stdin);
    	return p1 == p2 ? EOF : *p2++;
    }
    inline int read() {
    	int res = 0; char ch = gc();
    	while (ch < '0' || ch > '9') ch = gc();
    	while (ch >= '0' && ch <= '9') res = (res << 1) + (res << 3) + ch - '0', ch = gc();
    	return res;
    }
    void SegmentTree::update(int rt, int L, int R, int l, int r) {
    	if (L >= l && R <= r) ++add[rt];
    	else {
    		int mid = (L + R) >> 1;
    		if (l <= mid) update(rt << 1, L, mid, l, r);
    		if (r > mid) update(rt << 1 | 1, mid + 1, R, l, r);
    	}
    }
    int SegmentTree::query(int rt, int L, int R, int pos) {
    	if (L == R) return add[rt];
    	int mid = (L + R) >> 1;
    	if (pos <= mid) return add[rt] + query(rt << 1, L, mid, pos);
    	else return add[rt] + query(rt << 1 | 1, mid + 1, R, pos);
    }
    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[stop++] = v;
    			Tarjan(v, u);
    			low[u] = std::min(low[u], low[v]);
    			if (low[v] >= dfn[u]) {
    				int p; ++tot;
    				do {
    					p = stk[--stop];
    					bel[p].push_back(tot);
    				} while (p ^ v);
    				bel[u].push_back(tot);
    			}
    		} else low[u] = std::min(low[u], dfn[v]);
    	}
    }
    void rebuild() {
    	G.init();
    	for (int i = 1; i <= N; ++i) for (int j = 0; j < bel[i].size(); ++j)
    		G.insert(i, bel[i][j]);
    }
    void dfs1(int u) {
    	dep[u] = dep[fa[u]] + 1;
    	size[u] = 1;
    	for (int i = G.head[u]; ~i; i = G.edge[i].next) {
    		int v = G.edge[i].v;
    		if (v == fa[u]) continue;
    		fa[v] = u, dfs1(v);
    		size[u] += size[v];
    		if (!heavy[u] || size[heavy[u]] < size[v]) heavy[u] = v;
    	}
    }
    void dfs2(int u) {
    	dfn[u] = ++idx;
    	if (heavy[u]) {
    		top[heavy[u]] = top[u];
    		dfs2(heavy[u]);
    	}
    	for (int i = G.head[u]; ~i; i = G.edge[i].next) {
    		int v = G.edge[i].v;
    		if (v == fa[u] || v == heavy[u]) continue;
    		top[v] = v, dfs2(v);
    	}
    }
    //Rhein_E
    
  • 相关阅读:
    1048. Find Coins (25)
    return view 详解 MVC
    EF Power Tool 代码生成器 反向生成
    对新数据库使用 Code First
    一个成熟的网站的架构设计应该是这样的
    公司业务的设计思想感悟
    请给奋斗中的男人们一次机会
    大话西游感悟
    充满恶意的单词
    lisp的解释器
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10616370.html
Copyright © 2020-2023  润新知