zoukankan      html  css  js  c++  java
  • [Vani有约会]雨天的尾巴 /【模板】线段树合并

    我们在每一个节点上建立一个动态开点的线段树,最初只有根节点,线段树的叶节点下标维护的是物品类型,值为这种物品的个数。对于将x到y的路径上同时发放类型为w的物品,我们可以采用树上差分。这是对于点的差分,方法为:v[x] += 1, v[y] += 1, v[lca(x,y)] -= 1, v[ fa[lca(x, y)] -=1。对于普通的树上差分,就是再进行一遍dfs,统计前缀和。但是这道题的每一个节点都是一棵线段树,我们可以采用线段树合并来快速维护前缀和。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    pair<int, int> PII;
    
    int anc[N][22], dep[N];
    int n, m, root[N], head[N], cnt, idx, rg = 1e5, ans[N];
    
    struct Node {
    	int lc, rc;
    	int v, pos;
    } tr[N * 50];
    
    struct Edge {
    	int v, nxt;
    } e[N << 1];
    
    void AddEdge(int u, int v) {
    	e[++cnt].v = v;
    	e[cnt].nxt = head[u];
    	head[u] = cnt;
    }
    
    void dfs(int u, int father) {
    	anc[u][0] = father;
    	dep[u] = dep[father] + 1;
    	for(int i = 1; i <= 20; i ++) {
    		anc[u][i] = anc[anc[u][i - 1]][i - 1];
    	}
    	for(int i = head[u]; i; i = e[i].nxt) {
    		int v = e[i].v;
    		if( v == father)	continue;
    		dfs(v, u);
    	}
    }
    
    int lca(int x, int y) {
    	if( dep[x] < dep[y])	swap(x, y);
    	for(int i = 20; i >= 0; -- i ) {
    		if( dep[anc[x][i]] >= dep[y])
    			x = anc[x][i];
    	}
    	if( x == y)    return x;
    	for(int i = 20; i >= 0; -- i) {
    		 if( anc[x][i] != anc[y][i])
    		 	x = anc[x][i], y = anc[y][i];
    	} 
    	return anc[x][0];
    }
    
    void pushup(int u) {
    	if( tr[tr[u].lc].v >= tr[tr[u].rc].v) {
    		tr[u].v = tr[tr[u].lc].v;
    		tr[u].pos = tr[tr[u].lc].pos;
    	} else {
    		tr[u].v = tr[tr[u].rc].v;
    		tr[u].pos = tr[tr[u].rc].pos;
    	}
    }
    
    void insert(int u, int l, int r, int k, int v) {
    	if( l == r) {
    		tr[u].v += v;
    		tr[u].pos = l;
    		return ;
    	}
    	int mid = l + r >> 1;
    	if( k <= mid) {
    		if( !tr[u].lc)    tr[u].lc = ++ idx;
    		insert(tr[u].lc, l, mid, k, v);
    	}
    	else {
    		if( !tr[u].rc)    tr[u].rc = ++ idx;
    		insert(tr[u].rc, mid + 1, r, k, v);
    	}
    	pushup(u); 
    }
    
    int merge(int p, int q, int l, int r) {
    	if( !p || !q) 
    		return p + q;
    	if( l == r) {
    		tr[p].v += tr[q].v;
    		return p;
    	}
    	int mid = l + r >> 1;
    	tr[p].lc = merge(tr[p].lc, tr[q].lc, l, mid);
    	tr[p].rc = merge(tr[p].rc, tr[q].rc, mid + 1, r);
    	pushup(p);
    	return p;
    } 
    
    void Sum(int u, int fa) {
    	for(int i = head[u]; i; i = e[i].nxt) {
    		int v = e[i].v;
    		if( v == fa)	continue;
    		Sum(v, u);
    		root[u] = merge(root[u], root[v], 1, rg);
    	} 
    	if( tr[root[u]].v == 0)
    		ans[u] = 0;
    	else ans[u] = tr[root[u]].pos;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; i ++)	root[i] = i;
    	idx = n;
    	for(int i = 1; i < n;  i++) {
    		int u, v;
    		scanf("%d%d", &u, &v);
    		AddEdge(u, v), AddEdge(v, u); 
    	} 
    	dfs(1, 0);
    	while(m --) {
    		int u, v, w;
    		scanf("%d%d%d", &u, &v, &w);
    		insert(root[u], 1, rg, w, 1);
    		insert(root[v], 1, rg, w, 1);
    		int t = lca(u, v);
    		insert(root[t], 1, rg, w, -1);
    		insert(root[anc[t][0]], 1, rg, w, -1);
    	}
    	Sum(1, 0);
    	for(int i = 1; i <= n; i ++)
    	    printf("%d
    ", ans[i]);
    	return 0;
    } 
    
    
  • 相关阅读:
    Sql中CHARINDEX用法
    当没有用 EXISTS 引入子查询时,在选择列表中只能指定一个表达式
    Windows Server 2012 R2安装.NET4.7.2服务问题集合
    详细实例全面解析SQL存储过程
    MySQL 存储过程
    华为远程登陆配置
    数据平台环境部署(mysql、docker、nginx)
    ISIS的路由渗透
    华为模拟器的安装失败(出现40、41、42代码错误,Oracle VM VirtualBox安装出现严重错误以及一直处于开机状态)
    华为模拟器配置动态ospf--虚拟链路、rip重分布、static重分布
  • 原文地址:https://www.cnblogs.com/wyy0804/p/13773320.html
Copyright © 2011-2022 走看看