zoukankan      html  css  js  c++  java
  • [luogu P6041]「ACOI2020」布丁暗杀计划 题解

    嗯...感觉这题自己写恶心了。觉得复杂度应该是对的,但是不使用部分分做法过不了,不知为什么,估计是常数的问题。

    这题一开始觉得像dsu on tree,但是我不会。

    于是提供两种做法:

    1. bfs + 莫队(复杂度不正确,需使用部分分做法以及吸氧)
    2. bfs + 主席树(复杂度正确,需使用部分分,无需吸氧可过,但是在吸氧条件下跑得比上面的慢)

    先说一下基础的bfs问题。

    常见的树上问题好像用dfs序解决的比较多(可能只是蒟蒻做题太少)。

    利用dfs序,我们可以解决一些子树问题。利用dfs序上一个节点后面一段连续区间都在它的子树当中。

    但是这道题因为需要统计某一级的所有儿子的信息。请原谅蒟蒻没想到怎么用dfs来做。

    所以就有了bfs的做法,根据bfs的性质,我们可以想到,一个点的某一级的所有儿子反映到bfs序上一定也是一段连续的区间。

    利用倍增和二分,我们可以在(O(log\,n + 2\, log^2n ))的时间内求出每个询问所需要统计的bfs序区间,这个区间的k级祖先是同一个。第一个log是倍增找祖先的,第二个(log^2)是二分确定左端点的,第三个(log^2)是二分确定右端点的,二分的判断方式是看mid的k级祖先是不是也是该祖先,所以是(log^2)的。

    然后问题转化为给定若干组左端点右端点,在一段序列上统计一些信息。这一看就很莫队对不对。

    但是5e5的数据可能会阻挡一些人的想法,但是我比较头铁。本着数据一定是随机的想法,我交了一发发现只有第二个部分分不可过。

    所以果断打上部分分做法水过。

    部分分做法

    很显然一个节点的某一级的后代只会有一个。所以每个询问直接倍增找他的祖先,如果颜色相同答案就是该节点的(d),否则就是0 。

    但是本着对人民负责(懒得写别的题)的原则,我想了想怎么做复杂度是对的。

    然后发现处理序列问题某一段区间的问题可以前缀和,但是空间开不下,所以主席树也挺行的。

    然后把主席树的板子改一改就可做了。

    这个主席树是对颜色开的,即 (l==r)(sum[u]) 对应的就是在bfs序上 1 到 某一下标 颜色为 (l) 的权值的总和,支持单点修改单点查询即可。

    注意我的代码里主席树区间只是([1,n])的,如果有颜色大于(n)的话会错。码的时候忘了,现在懒得改了。正确的做法是读入时记录一下最大的颜色mx,然后建树建([1,mx])

    这样我们就可以用(O(log \,n))复杂度做到在线回答询问。

    复杂度瓶颈应该是(O(q \, log^2n)),应该是可过的,但是不知道为何链的情况就是有两个点会T 。

    感觉是常数的问题,看到题解里(O(n \, log \, n))长剖的大兄弟都过不去我就不想调了。

    code1(bfs + 莫队):

    #include <queue>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn = 5e5 + 10;
    inline int read() {
    	int s = 0, w = 1;
    	char c = getchar();
    	while (c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
    	while (c >= '0' && c <= '9') s = s * 10 + c - '0', c = getchar();
    	return s * w;
    }
    struct edge {
    	int nex;
    	int to;
    } e[maxn << 1];
    int head[maxn];
    int tot;
    void Add(int u, int v) {
    	e[++tot] = (edge) { head[u], v }, head[u] = tot;
    }
    int n, m;
    int bfsclock;
    int deg[maxn];
    int c[maxn];
    int d[maxn];
    int bfn[maxn];
    int idex[maxn];
    int sum[maxn];
    int ans[maxn];
    int fa[maxn][26];
    int bel[maxn];
    struct node {
    	int c;
    	int l;
    	int r;
    	int id;
    	bool operator < (const node &x) const {
    		if (bel[l] != bel[x.l]) return l < x.l;
    		if (bel[l] & 1) return r < x.r;
    		return r > x.r;
    	}
    } Q[maxn];
    int Find(int u, int k) {
    	int res = 0;
    	while (k) {
    		if (k & 1) u = fa[u][res];
    		res++, k >>= 1;
    	}
    	return u;
    }
    void PreWork(int u) {
    	for (int i = 1; i <= 25; ++i) {
    		fa[u][i] = fa[fa[u][i - 1]][i - 1];
    	}
    	for (int i = head[u]; i; i = e[i].nex) {
    		int v = e[i].to;
    		if (v == fa[u][0]) continue;
    		PreWork(v);
    	}
    }
    queue<int> que;
    bool vis[maxn];
    void bfs() {
    	que.push(1);
    	while (!que.empty()) {
    		int u = que.front();
    		que.pop();
    		bfn[u] = ++bfsclock;
    		idex[bfsclock] = u;
    		for (int i = head[u]; i; i = e[i].nex) {
    			int v = e[i].to;
    			if (!vis[v]) que.push(v), vis[v] = 1;
    		}
    	}
    }
    void init() {
    	int s = sqrt(n), cnt = 0;
    	for (int l = 1, r; l + s - 1 <= n; l += s) {
    		r = l + s - 1;
    		cnt++;
    		for (int i = l; i <= r; ++i) bel[i] = cnt;
    	}
    	if (s * cnt < n) {
    		int l = s * cnt + 1;
    		int r = n;
    		cnt++;
    		for (int i = l; i <= r; ++i) bel[i] = cnt;
    	}
    }
    void putin(int x) {
    	sum[c[idex[x]]] += d[idex[x]];
    }
    void del(int x) {
    	sum[c[idex[x]]] -= d[idex[x]];
    }
    int main() {
    	cin >> n >> m;
    	init();
    	for (int i = 1; i <= n; ++i) c[i] = read();
    	for (int i = 1; i <= n; ++i) d[i] = read();
    	for (int i = 2; i <= n; ++i) {
    		fa[i][0] = read();
    		Add(fa[i][0], i);
    		deg[i]++;
    		deg[fa[i][0]]++;
    	}
    	PreWork(1);
    	bfs();
    	int cnt1 = 0, cnt2 = 0;
    	for (int i = 1; i <= n; ++i) {
    		if (deg[i] == 1) cnt1++;
    		if (deg[i] == 2) cnt2++;
    	}
    	if (cnt1 == 2 && cnt2 == n - 2) {
    		for (int i = 1; i <= m; ++i) {
    			int u = read(), k = read();
    			int faa = Find(u, k);
    			if (c[u] == c[faa]) cout << d[u] << '
    ';
    			else cout << 0 << '
    ';
    		}
    		return 0;
    	}
    	for (int i = 1; i <= m; ++i) {
    		int u = read(), k = read();	
    		int faa = Find(u, k);
    		int l = 1, r = bfn[u] - 1, mid;
    		Q[i].id = i;
    		Q[i].c = c[faa];
    		while (l <= r) {
    			mid = (l + r) >> 1;
    			if (Find(idex[mid], k) == faa) r = mid - 1;
    			else l = mid + 1;
    		}
    		Q[i].l = l;
    		l = bfn[u] + 1, r = n;
    		while (l <= r) {
    			mid = (l + r) >> 1;
    			if (Find(idex[mid], k) == faa) l = mid + 1;
    			else r = mid - 1;
    		}
    		Q[i].r = l - 1;
    	}
    	sort(Q + 1, Q + 1 + m);
    	for (int i = 1, l = 1, r = 0; i <= m; ++i) {
    		node q = Q[i];
    		while (l > q.l) putin(--l);
    		while (r < q.r) putin(++r);
    		while (l < q.l) del(l++);
    		while (r > q.r) del(r--);
    		ans[q.id] = sum[q.c];
    	}
    	for (int i = 1; i <= m; ++i) cout << ans[i] << '
    ';
    	return 0;
    }
    

    code2(bfs + 主席树):

    #include <queue>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn = 5e5 + 10;
    inline int read() {
    	int s = 0, w = 1;
    	char c = getchar();
    	while (c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
    	while (c >= '0' && c <= '9') s = s * 10 + c - '0', c = getchar();
    	return s * w;
    }
    struct edge {
    	int nex;
    	int to;
    } e[maxn << 1];
    int head[maxn];
    int tot;
    void Add(int u, int v) {
    	e[++tot] = (edge) { head[u], v }, head[u] = tot;
    }
    int n, m;
    int bfsclock;
    int son[maxn];
    int c[maxn];
    int d[maxn];
    int bfn[maxn];
    int idex[maxn];
    int fa[maxn][26];
    int Find(int u, int k) {
    	int res = 0;
    	while (k) {
    		if (k & 1) u = fa[u][res];
    		res++, k >>= 1;
    	}
    	return u;
    }
    void PreWork(int u) {
    	for (int i = 1; i <= 25; ++i) {
    		fa[u][i] = fa[fa[u][i - 1]][i - 1];
    	}
    	for (int i = head[u]; i; i = e[i].nex) {
    		int v = e[i].to;
    		if (v == fa[u][0]) continue;
    		PreWork(v);
    	}
    }
    queue<int> que;
    bool vis[maxn];
    void bfs() {
    	que.push(1);
    	while (!que.empty()) {
    		int u = que.front();
    		que.pop();
    		bfn[u] = ++bfsclock;
    		idex[bfsclock] = u;
    		for (int i = head[u]; i; i = e[i].nex) {
    			int v = e[i].to;
    			if (!vis[v]) que.push(v), vis[v] = 1;
    		}
    	}
    }
    
    //主席树
    int cnt;
    int rt[maxn];
    ll sum[maxn << 5];
    int ls[maxn << 5];
    int rs[maxn << 5];
    int build(int l, int r) {
    	int root = ++cnt;
    	sum[root] = 0;
    	if (l == r) return root;
    	int mid = (l + r) >> 1;
    	ls[root] = build(l, mid);
    	rs[root] = build(mid + 1, r);
    	return root;
    }
    int modify(int pre, int l, int r, int pos, int val) {
    	int root = ++cnt;
    	ls[root] = ls[pre];
    	rs[root] = rs[pre];
    	sum[root] = sum[pre] + val;
    	if (l == r) return root;
    	int mid = (l + r) >> 1;
    	if (pos <= mid) ls[root] = modify(ls[pre], l, mid, pos, val);
    	else rs[root] = modify(rs[pre], mid + 1, r, pos, val);
    	return root;
    }
    int query(int u, int v, int l, int r, int pos) {
    	if (l == r) return sum[v] - sum[u];
    	int mid = (l + r) >> 1;
    	if (pos <= mid) return query(ls[u], ls[v], l, mid, pos);
    	return query(rs[u], rs[v], mid + 1, r, pos);
    }
    
    int main() {
    	cin >> n >> m;
    	for (int i = 1; i <= n; ++i) c[i] = read();
    	for (int i = 1; i <= n; ++i) d[i] = read();
    	for (int i = 2; i <= n; ++i) {
    		fa[i][0] = read();
    		Add(fa[i][0], i);
    		son[fa[i][0]]++;
    	}
    	PreWork(1);
    	bfs();
    	int flag = 1;
    	for (int i = 1; i <= n; ++i) {
    		if (son[i] > 1) flag = 0;
    	}
    	if (flag) {
    		for (int i = 1; i <= m; ++i) {
    			int u = read(), k = read();
    			int faa = Find(u, k);
    			if (c[u] == c[faa]) cout << d[u] << '
    ';
    			else cout << 0 << '
    ';
    		}
    		return 0;
    	}
    	rt[0] = build(1, n);
    	for (int i = 1; i <= n; ++i) {
    		rt[i] = modify(rt[i - 1], 1, n, c[idex[i]], d[idex[i]]);
    	}
    	for (int i = 1; i <= m; ++i) {
    		int u = read(), k = read();	
    		int faa = Find(u, k);
    		if (!faa) {
    		    cout << 0 << '
    ';
    		    continue;
    		}
    		int l = 1, r = bfn[u] - 1, mid;
    		int L, R;
    		while (l <= r) {
    			mid = (l + r) >> 1;
    			if (Find(idex[mid], k) == faa) r = mid - 1;
    			else l = mid + 1;
    		}
    		L = l;
    		l = bfn[u] + 1, r = n;
    		while (l <= r) {
    			mid = (l + r) >> 1;
    			if (Find(idex[mid], k) == faa) l = mid + 1;
    			else r = mid - 1;
    		}
    		R = l - 1;
    		printf("%d
    ", query(rt[L - 1], rt[R], 1, n, c[faa]));
    	}
    	return 0;
    }
    
  • 相关阅读:
    Erlang 督程 启动和结束子进程
    cocos2d-x 3.0 内存管理机制
    c语言基本数据类型
    4星|《剑桥中国经济史:古代到19世纪》:经济学视角看中国古代史
    孟晚舟三种结局;共享单车大败局;失业潮不会来:4星|《财经》2018年第30期
    2018左其盛差评榜,罕见的差书榜
    2018左其盛好书榜,没见过更好的榜单
    罗振宇时间的朋友2018跨年演讲中最重要的35句话
    中国土地制度与房价走势相关9本书
    2星|水木然《世界在变软》:肤浅的朋友圈鸡汤文
  • 原文地址:https://www.cnblogs.com/Zfio/p/14006456.html
Copyright © 2011-2022 走看看