zoukankan      html  css  js  c++  java
  • 【BZOJ3772】精神污染

    题目

    传送门

    题解&算法

    看完题后千万不要被概率吓到

    这题的难点在于求与一条路径被另一条路径包含的总数量

    很明显如果以x,a被x,b包含, 那么(in[b] <= in[a] < out[b]), 且(out[a] > out[b])

    于是我们可以用主席树来处理一下, 建树时差分一下, 查询时也是前缀和差分

    代码

    #include <iostream>
    #include <cstdlib>
    #include <vector>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 100010, M = 200010;
    
    typedef long long LL;
    
    struct edge
    {	int from, to;
    	edge() { }
    	edge(int _1, int _2) : from(_1), to(_2) { }
    } edges[M];
    
    int head[N], nxt[M], tot;
    
    inline void init()
    {	memset(head, -1, sizeof(head));
    	tot = 0;
    }
    
    inline void add_edge(int x, int y)
    {	edges[tot] = edge(x, y);
    	nxt[tot] = head[x];
    	head[x] = tot++;
    	edges[tot] = edge(y, x);
    	nxt[tot] = head[y];
    	head[y] = tot++;
    }
    
    int dep[N], father[N][17];
    int in[N], out[N], dfs_clock;
    
    void dfs(int x)
    {	for (int i = 1; (1<<i) <= dep[x]; i++)
    		father[x][i] = father[father[x][i-1]][i-1];
    	in[x] = ++dfs_clock;
    	for (int i = head[x]; ~i; i = nxt[i])
    	{	edge & e = edges[i];
    		if (e.to != father[x][0])
    		{	dep[e.to] = dep[x] + 1;
    			father[e.to][0] = x;
    			dfs(e.to);
    		}
    	}
    	out[x] = ++dfs_clock; //少了++样例输出就变成-1/1了
    }
    
    int getLCA(int x, int y)
    {	if (dep[x] > dep[y]) swap(x, y);
    	int d = dep[y] - dep[x];
    	for (int i = 0; (1<<i) <= d; i++)
    		if (d & (1<<i)) y = father[y][i];
    	if (x == y) return x;
    	for (int i = 16; i >= 0; i--)
    	{	if (father[x][i] != father[y][i])
    		{	x = father[x][i];
    			y = father[y][i];
    		}
    	}
    	return father[x][0]; 
    }
    
    int root[N * 2];
    
    struct SegmentTree
    {	int ls[3800010], rs[3800010], sz;
    	int sumv[3800010];//开40000010会MLE
    
    	inline void cpynode(int x, int y)
    	{	ls[x] = ls[y];
    		rs[x] = rs[y];
    		sumv[x] = sumv[y];
    	}
    
    	void insert(int & cur, int pre, int l, int r, int p, int val)
    	{	cur = ++sz;
    		cpynode(cur, pre);
    		sumv[cur] +=  val;
    		if (l == r) return;
    		int mid = (l + r) >> 1;
    		if (p <= mid)
    			insert(ls[cur], ls[pre], l, mid, p, val);
    		else
    			insert(rs[cur], rs[pre], mid+1, r, p, val);
    	}
    
    	LL query(int x, int y, int z, int k, int l, int r, int ql, int qr)
    	{	if (ql == l && qr == r)
    			return sumv[x] + sumv[y] - sumv[z] - sumv[k];
    		int mid = (l + r) >> 1;
    		if (qr <= mid)
    			return query(ls[x], ls[y], ls[z], ls[k], l, mid, ql, qr);
    		else if (ql > mid)
    			return query(rs[x], rs[y], rs[z], rs[k], mid+1, r, ql, qr);
    		else
    			return query(ls[x], ls[y], ls[z], ls[k], l, mid, ql, mid) + 
    				   query(rs[x], rs[y], rs[z], rs[k], mid+1, r, mid+1, qr);
    	}
    } st;
    
    LL gcd(LL a, LL b) { return b == 0 ? a : gcd(b, a % b); }
    
    int n, m;
    
    struct Data
    {	int x, y;
    	Data() { }
    	Data(int _1, int _2) : x(_1), y(_2) { }
    } L[N];
    
    bool cmp(Data a, Data b) { return a.x != b.x ? a.x < b.x : a.y < b.y; }
    
    vector<int> vec[N];
    
    void dfs_2(int x)
    {	root[x] = root[father[x][0]];
    	for (int i = 0; i < vec[x].size(); i++)
    		st.insert(root[x], root[x], 1, dfs_clock, in[vec[x][i]], 1),
    		st.insert(root[x], root[x], 1, dfs_clock, out[vec[x][i]], -1);
    	for (int i = head[x]; ~i; i = nxt[i])
    	{	edge & e = edges[i];
    		if (e.to != father[x][0])
    			dfs_2(e.to);
    	}
    }
    
    int main()
    {	scanf("%d %d", &n, &m);
    	init();
    	for (int i = 1; i < n; i++)
    	{	int x, y;
    		scanf("%d %d", &x, &y);
    		add_edge(x, y);
    	}
    	father[1][0] = 0;
    	dep[1] = 1;
    	dfs(1);
    	for (int i = 1; i <= m; i++)
    		scanf("%d %d", &L[i].x, &L[i].y), 
    		vec[L[i].x].push_back(L[i].y);
    	dfs_2(1);
    	sort(L + 1, L + m + 1, cmp);
    	LL ans1 = 0, ans2 = (LL) m * (m-1) / 2;
    	for (int i = 1; i <= m; i++)
    	{	int lca = getLCA(L[i].x, L[i].y);
    		ans1 += st.query(root[L[i].x], root[L[i].y], root[lca], root[father[lca][0]], 1, dfs_clock, in[lca], in[L[i].x]) + 
    				st.query(root[L[i].x], root[L[i].y], root[lca], root[father[lca][0]], 1, dfs_clock, in[lca], in[L[i].y]) -  //刚开始减号打成加号调了很久啊。。。
    				st.query(root[L[i].x], root[L[i].y], root[lca], root[father[lca][0]], 1, dfs_clock, in[lca], in[lca])
    				 - 1;
    	}
    	int j;
    	for (int i = 1; i <= m; i = j)
    		for (j = i; L[i].x == L[j].x && L[i].y == L[j].y; j++)
    			ans1 -= (LL) (j - i) * (j - i - 1) / 2//一定要减掉重复!!!!!!!!!!!!!
    	LL d = gcd(ans1, ans2);
    	ans1 /= d, ans2 /= d;
    	printf("%lld/%lld", ans1, ans2);
    	return 0;
    }
    

    恶心的地方

    1. 卡空间
    2. 路径有重复
  • 相关阅读:
    for else 使用方法
    random 模块常用方法总结
    CPU使用率高,top等命令查找不到使用CPU高的进程怎么办
    查看CPU核数方法
    PyCharm安装第三方库如Requests
    python-login
    Edit Distance
    redhat nginx随机启动脚本
    vue-cli脚手架build目录中的webpack.base.conf.js配置文件
    vue-cli脚手架build目录下utils.js工具配置文件详解
  • 原文地址:https://www.cnblogs.com/2016gdgzoi509/p/9152289.html
Copyright © 2011-2022 走看看