• 【HNOI2015】开店


    题面

    题解

    树链剖分 + 主席树

    先考虑一个简单一点的问题:

    【LNOI2014】LCA

    我们考察(dep[mathrm{LCA}(i, x)])的性质,发现它是(i)(x)的链交的长度。

    那么对每个(i)所在的链打一个区间加标记,询问时算一下(x)所在的链的区间和即可。

    如果有(l leq i leq r)的限制,就进行离线处理即可。

    接下来再考虑一个问题:

    给定一棵(n)个节点的树,边有边权。

    多次询问,每次给定(x),求(sum_{i = 1}^n dis(i, x))

    因为(dis(i, x) = dis[i] + dis[x] - 2 imes dis[mathrm{LCA}(i, x)]),其中(dis[i])表示(i)到根节点的距离,于是又变成了前面那一个问题。

    但是这一题有年龄限制,所以用可持久化线段树维护一下即可。

    这一题的线段树要标记永久化,不然空间会炸。​

    时间复杂度(mathrm{O}(nlog_2^2 n))

    动态点分治

    动态点分治也可以维护距离和,就是维护两个数组:一个维护子树中所有点到这个点的距离和以及子树中的所有点到这个点到这个点的父亲节点的距离和就可以了,计算答案的时候两个相减即可。

    然后发现这道题目不太一样,带了年龄,于是不能用数组。

    对于每个点开一个vector,记录上面的信息,同时记录年龄,然后将每个vector按照年龄从小到大排序,并求出前缀和,然后二分查找即可。

    时间复杂度(mathrm{O}(nlog_2^2 n))

    代码

    树链剖分 + 主席树

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #define RG register
    
    template<typename T>
    inline T read()
    {
    	T data = 0, w = 1; char ch = getchar();
    	while(ch != '-' && (!isdigit(ch))) ch = getchar();
    	if(ch == '-') w = -1, ch = getchar();
    	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
    	return data * w;
    }
    
    const int maxn(200000);
    struct edge { int next, to, dis; } e[maxn << 1];
    int head[maxn], e_num, n, Q, A, age[maxn];
    
    inline void add_edge(int from, int to, int dis)
    {
    	e[++e_num] = (edge) {head[from], to, dis};
    	head[from] = e_num;
    }
    
    int size[maxn], fa[maxn], heavy[maxn], dis[maxn];
    void dfs(int x)
    {
    	size[x] = 1;
    	for(RG int i = head[x]; i; i = e[i].next)
    	{
    		int to = e[i].to; if(to == fa[x]) continue;
    		fa[to] = x, dis[to] = dis[x] + e[i].dis;
    		dfs(to); size[x] += size[to];
    		if(size[heavy[x]] < size[to]) heavy[x] = to;
    	}
    }
    
    int pos[maxn], belong[maxn], cnt_pos[maxn], cnt;
    void dfs(int x, int chain)
    {
    	cnt_pos[pos[x] = ++cnt] = x;
    	belong[x] = chain;
    	if(!heavy[x]) return;
    	dfs(heavy[x], chain);
    	for(RG int i = head[x]; i; i = e[i].next)
    	{
    		int to = e[i].to;
    		if(to == fa[x] || to == heavy[x]) continue;
    		dfs(to, to);
    	}
    }
    
    inline int cmp(int x, int y) { return age[x] < age[y]; }
    int son[2][maxn << 6], cur;
    long long sum[maxn << 6], lazy[maxn << 6];
    void build(int &x, int l = 1, int r = n)
    {
    	x = ++cur; if(l == r) return;
    	int mid = (l + r) >> 1;
    	build(son[0][x], l, mid);
    	build(son[1][x], mid + 1, r);
    }
    
    void update(int &x, int ql, int qr, int l = 1, int r = n)
    {
    	++cur; son[0][cur] = son[0][x], son[1][cur] = son[1][x];
    	sum[cur] = sum[x], lazy[cur] = lazy[x]; x = cur;
    	sum[x] += dis[cnt_pos[qr]] - dis[fa[cnt_pos[ql]]];
    	if(ql == l && r == qr) return (void) (++lazy[x]);
    	int mid = (l + r) >> 1;
    	if(ql <= mid) update(son[0][x], ql, std::min(qr, mid), l, mid);
    	if(mid < qr) update(son[1][x], std::max(mid + 1, ql), qr, mid + 1, r);
    }
    
    long long query(int x, int ql, int qr, long long ex = 0, int l = 1, int r = n)
    {
    	if(ql == l && r == qr) return sum[x] + 1ll * ex *
    		(dis[cnt_pos[qr]] - dis[fa[cnt_pos[ql]]]);
    	int mid = (l + r) >> 1; long long ans = 0; ex += lazy[x];
    	if(ql <= mid) ans = query(son[0][x], ql, std::min(qr, mid), ex, l, mid);
    	if(mid < qr) ans += query(son[1][x], std::max(mid + 1, ql), qr, ex, mid + 1, r);
    	return ans;
    }
    
    long long sdis[maxn];
    int root[maxn], id[maxn], S[maxn];
    long long calc(int x, int k)
    {
    	long long ans = 0;
    	while(belong[x] != 1) ans += query(root[k], pos[belong[x]], pos[x]),
    		x = fa[belong[x]];
    	return ans + query(root[k], 1, pos[x]);
    }
    
    int main()
    {
    	n = read<int>(), Q = read<int>(), A = read<int>();
    	for(RG int i = 1; i <= n; i++) id[i] = i, S[i] = age[i] = read<int>();
    	std::sort(S + 1, S + n + 1), std::sort(id + 1, id + n + 1, cmp);
    	for(RG int i = 1, a, b, c; i < n; i++)
    		a = read<int>(), b = read<int>(), c = read<int>(),
    		add_edge(a, b, c), add_edge(b, a, c);
    	dfs(1); dfs(1, 1); build(root[0]);
    	for(RG int i = 1; i <= n; i++)
    	{
    		int x = id[i]; sdis[i] = sdis[i - 1] + dis[x];
    		root[i] = root[i - 1];
    		while(belong[x] != 1) update(root[i], pos[belong[x]], pos[x]),
    			x = fa[belong[x]];
    		update(root[i], 1, pos[x]);
    	}
    	for(long long ans = 0; Q--;)
    	{
    		int x = read<int>();
    		long long a = read<long long>(), b = read<long long>();
    		int L = std::min((ans + a) % A, (ans + b) % A);
    		int R = std::max((ans + a) % A, (ans + b) % A);
    		L = std::lower_bound(S + 1, S + n + 1, L) - S;
    		R = std::lower_bound(S + 1, S + n + 1, R + 1) - S - 1;
    		ans = 1ll * (R - L + 1) * dis[x] + sdis[R] - sdis[L - 1]
    			- 2ll * (calc(x, R) - calc(x, L - 1));
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

    动态点分治

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #include<vector>
    #define RG register
    
    inline int read()
    {
    	int data = 0, w = 1; char ch = getchar();
    	while(ch != '-' && (!isdigit(ch))) ch = getchar();
    	if(ch == '-') w = -1, ch = getchar();
    	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
    	return data * w;
    }
    
    const int maxn(150010), LogN(20);
    struct node { int val; long long dis, sum; };
    inline bool operator < (const node &lhs, const node &rhs)
    {
    	return lhs.val == rhs.val ? lhs.dis == rhs.dis ?
    		lhs.sum < rhs.sum : lhs.dis < rhs.dis : lhs.val < rhs.val;
    }
    
    std::vector<node> va[maxn], vb[maxn];
    struct edge { int next, to, dis; } e[maxn << 1];
    int head[maxn], e_num, age[maxn], n, Q, A, Log[maxn << 1], fa[maxn];
    int pos[maxn], cnt, size[maxn], _max, SIZE, root, vis[maxn];
    long long dep[maxn], ST[LogN][maxn << 1];
    inline void add_edge(int from, int to, int dis)
    {
    	e[++e_num] = (edge) {head[from], to, dis};
    	head[from] = e_num;
    }
    
    void dfs(int x, int fa)
    {
    	ST[0][pos[x] = ++cnt] = dep[x];
    	for(RG int i = head[x]; i; i = e[i].next)
    	{
    		int to = e[i].to; if(to == fa) continue;
    		dep[to] = dep[x] + e[i].dis; dfs(to, x);
    		ST[0][++cnt] = dep[x];
    	}
    }
    
    long long Dis(int x, int y)
    {
    	int t = dep[x] + dep[y];
    	x = pos[x], y = pos[y]; if(x > y) std::swap(x, y);
    	int k = Log[y - x + 1];
    	return t - 2 * std::min(ST[k][x], ST[k][y - (1 << k) + 1]);
    }
    
    void getRoot(int x, int fa)
    {
    	size[x] = 1; int _ = 0;
    	for(RG int i = head[x]; i; i = e[i].next)
    	{
    		int to = e[i].to; if(vis[to] || to == fa) continue;
    		getRoot(to, x); size[x] += size[to]; _ = std::max(_, size[to]);
    	}
    	_ = std::max(_, SIZE - size[x]);
    	if(_ < _max) root = x, _max = _;
    }
    
    void Solve(int x)
    {
    	vis[x] = 1;
    	for(RG int i = head[x]; i; i = e[i].next)
    	{
    		int to = e[i].to; if(vis[to]) continue;
    		SIZE = _max = size[to], root = to; getRoot(to, x);
    		fa[root] = x; Solve(root);
    	}
    }
    
    long long query(int x, int a)
    {
    	long long ans = 0;
    	for(RG int i = x; i; i = fa[i])
    	{
    		int t = std::lower_bound(va[i].begin(), va[i].end(),
    				(node) {a, 0, 0}) - va[i].begin() - 1;
    		ans += va[i][t].sum + t * Dis(i, x);
    	}
    
    	for(RG int i = x; fa[i]; i = fa[i])
    	{
    		int t = std::lower_bound(vb[i].begin(), vb[i].end(),
    				(node) {a, 0, 0}) - vb[i].begin() - 1;
    		ans -= vb[i][t].sum + t * Dis(fa[i], x);
    	}
    	return ans;
    }
    
    int main()
    {
    	n = read(), Q = read(), A = read();
    	for(RG int i = 1; i <= n; i++) age[i] = read();
    	for(RG int i = 1, a, b, c; i < n; i++)
    		a = read(), b = read(), c = read(),
    		add_edge(a, b, c), add_edge(b, a, c);
    	dfs(1, 0); Log[0] = -1;
    	for(RG int i = 1; i <= cnt; i++) Log[i] = Log[i >> 1] + 1;
    	for(RG int i = 1; i <= Log[cnt]; i++)
    		for(RG int j = 1; j <= cnt - (1 << i) + 1; j++)
    			ST[i][j] = std::min(ST[i - 1][j], ST[i - 1][j + (1 << (i - 1))]);
    	SIZE = _max = n; getRoot(1, 0); Solve(root);
    	for(RG int i = 1; i <= n; i++) for(RG int j = i; j; j = fa[j])
    		va[j].push_back((node) {age[i], Dis(i, j), 0}),
    		vb[j].push_back((node) {age[i], Dis(i, fa[j]), 0});
    	for(RG int i = 1; i <= n; i++)
    	{
    		va[i].push_back((node) {-1, 0, 0});
    		vb[i].push_back((node) {-1, 0, 0});
    		va[i].push_back((node) {1 << 30, 0, 0});
    		vb[i].push_back((node) {1 << 30, 0, 0});
    		std::sort(va[i].begin(), va[i].end());
    		std::sort(vb[i].begin(), vb[i].end());
    		for(RG int j = 1; j < (int)va[i].size(); j++)
    			va[i][j].sum = va[i][j - 1].sum + va[i][j].dis;
    		for(RG int j = 1; j < (int)vb[i].size(); j++)
    			vb[i][j].sum = vb[i][j - 1].sum + vb[i][j].dis;
    	}
    	for(long long ans = 0; Q--;)
    	{
    		int x = read(), a = read(), b = read();
    		a = (ans + a) % A, b = (ans + b) % A;
    		if(a > b) std::swap(a, b);
    		printf("%lld
    ", ans = query(x, b + 1) - query(x, a));
    	}
    	return 0;
    }
    
  • 相关阅读:
    What are the difference between DDL, DML and DCL commands?
    Dingjun123 :使用Partitioned Outer Join实现稠化报表
    Oracle Clusters
    Google实验室能力倾向测试(第一题及解答)
    搜索系统中基于字典的逆向中文分词
    vc++ 深入浅出 窗口创建过程
    计算机网络基础知识1
    线性代数学习之对称矩阵与矩阵的SVD分解
    珍爱生命
    str2hash
  • 原文地址:https://www.cnblogs.com/cj-xxz/p/10416276.html
Copyright © 2020-2023  润新知