zoukankan      html  css  js  c++  java
  • 「Luogu4556」Vani有约会-雨天的尾巴

    「Luogu4556」Vani有约会-雨天的尾巴

    传送门
    很显然可以考虑树上差分+桶,每次更新一条链就是把这条链上的点在桶对应位置打上 (1) 的标记,
    最后对每个点取桶中非零值的位置作为答案即可,如果全都是 (0) 就输出 (0) ,这样的时间复杂度和空间复杂度都是 (O(nm))
    考虑优化这一个算法:
    我们考虑用权值线段树来代替桶
    我们可以考虑用树剖的方式来更新一条链,那么就变成了每次在值域线段树上对 (log) 段区间差分,
    需要注意的是,我们把正标记打在深度低的那一边,因为我们可以这样按照 ( ext{dfs}) 序算答案
    小小的证明:

    • 对于不贡献当前点的询问,这样做显然是没有影响的
    • 对于贡献当前点的询问,它的贡献一定回被重链的顶点打好标记,也一定会被重链底的轻儿子消除

    这样一来就得到了一种类似于离线算法的统计答案方法,于是,线段树也就只要开一棵
    代码:

    #include <cstdio>
    #include <vector>
    #define rg register
    #define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
    using namespace std;
    template < class T > inline void read(T& s) {
    	s = 0; int f = 0; char c = getchar();
    	while ('0' > c || c > '9') f |= c == '-', c = getchar();
    	while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    	s = f ? -s : s;
    }
    
    const int _ = 1e5 + 5;
    
    int tot, head[_], nxt[_ << 1], ver[_ << 1];
    inline void Add_edge(int u, int v)
    { nxt[++tot] = head[u], head[u] = tot, ver[tot] = v; }
    
    int n, m, ans[_]; vector < int > vec[_];
    int dep[_], siz[_], son[_], fa[_];
    int dfn[_], rev[_], top[_];
    struct node { int mx, pos; } t[_ << 2];
    
    inline int lc(int p) { return p << 1; }
    
    inline int rc(int p) { return p << 1 | 1; }
    
    inline void pushup(int p) {
    	if (t[lc(p)].mx >= t[rc(p)].mx)
    		t[p].mx = t[lc(p)].mx, t[p].pos = t[lc(p)].pos;
    	else
    		t[p].mx = t[rc(p)].mx, t[p].pos = t[rc(p)].pos;
    }
    
    inline void build(int p = 1, int l = 1, int r = 100000) {
    	if (l == r) { t[p].mx = 0, t[p].pos = l; return ; }
    	int mid = (l + r) >> 1;
    	build(lc(p), l, mid), build(rc(p), mid + 1, r), pushup(p);
    }
    
    inline void update(int x, int v, int p = 1, int l = 1, int r = 100000) {
    	if (l == r) { t[p].mx += v; return ; }
    	int mid = (l + r) >> 1;
    	if (x <= mid) update(x, v, lc(p), l, mid);
    	else update(x, v, rc(p), mid + 1, r);
    	pushup(p);
    }
    
    inline void uptRange(int x, int y, int z) {
    	int fx = top[x], fy = top[y];
    	while (fx != fy) {
    		if (dep[fx] < dep[fy]) swap(x, y), swap(fx, fy);
    		vec[dfn[fx]].push_back(z), vec[dfn[x] + 1].push_back(-z);
    		x = fa[fx], fx = top[x];
    	}
    	if (dep[x] > dep[y]) swap(x, y);
    	vec[dfn[x]].push_back(z), vec[dfn[y] + 1].push_back(-z);
    }
    
    inline void dfs(int u, int f) {
    	siz[u] = 1, dep[u] = dep[f] + 1, fa[u] = f;
    	for (rg int i = head[u]; i; i = nxt[i]) {
    		int v = ver[i]; if (v == f) continue;
    		dfs(v, u), siz[u] += siz[v];
    		if (siz[v] > siz[son[u]]) son[u] = v;
    	}
    }
    
    inline void dfs(int u, int f, int topf) {
    	top[rev[dfn[u] = ++dfn[0]] = u] = topf;
    	if (son[u]) dfs(son[u], u, topf);
    	for (rg int i = head[u]; i; i = nxt[i]) {
    		int v = ver[i]; if (v == f || v == son[u]) continue;
    		dfs(v, u, v);
    	}
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
    	file("cpp");
    #endif
    	read(n), read(m);
    	for (rg int u, v, i = 1; i < n; ++i)
    		read(u), read(v), Add_edge(u, v), Add_edge(v, u);
    	dfs(1, 0), dfs(1, 0, 1), build();
    	for (rg int x, y, z; m--; ) read(x), read(y), read(z), uptRange(x, y, z);
    	for (rg int i = 1; i <= n; ++i) {
    		for (rg int j : vec[i]) if (j > 0) update(j, 1); else update(-j, -1);
    		ans[rev[i]] = t[1].mx > 0 ? t[1].pos : 0;
    	}
    	for (rg int i = 1; i <= n; ++i) printf("%d
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    SQL SERVER 查看sql语句性能与执行时间
    SQL SERVER 一组数据按规律横着放置,少则补空,如人员按一进一出的规律,进出为一组,缺少的补null
    SQL SERVER 子查询使用Order By;按In排序
    SQL SERVER 字符拆分列为多行
    SQL SERVER 字符合并多行为一列
    SQL SERVER pivot(行转列),unpivot(列转行)
    ASP.NET ViewState详解
    数字签名
    ASP.NET Form身份验证方式详解
    细说进程、应用程序域与上下文之间的关系
  • 原文地址:https://www.cnblogs.com/zsbzsb/p/12190198.html
Copyright © 2011-2022 走看看