题意分析:
结点1为根结点,初始时已经染过颜色。给出N - 1行,第i行代表第i + 1个结点的父亲结点是哪个结点。
现在给出最多1e5个结点,1e5个查询。查询操作分两种,Q X代表查询结点X的最近的被染色的父或者祖先结点被染色的编号,M X代表对结点X染色。
问:所有查询完之后,Q查询的编号值之和为多少?
解题思路:
最坏情况下为整棵树成为一条链,单纯使用并查集查询复杂度最坏1e10。然而单纯使用并查集也能过,这题估计数据水。(或者谁告诉我下,1e10八秒是无压力跑的,那我也就没什么意见了。)
正解应该是将查询存下来,存下每次Q操作的查询时间和查询结点,用qt和qv数组存储。另外设置mark数组存储这个结点被染色的最快时间。然后我们倒着进行查询操作,当当前查询的结点最早被染色时间小于当前时间时,就可以返回这个结点了,否则进行路径压缩(进入否则,说明这个结点的最快被染色时间大于查询时间,而我们是倒着进行操作的,说明后继的查询时间都比当前的查询时间小,不管怎么样,这个结点都不会被染色了,无用,直接拿来压缩掉)
还有一种解法就是使用线段树来解这道题,利用dfs序来维护。具体是队友做出来了,我也不大懂怎么搞= =,膜拜ORZ 具体做法见下↓
个人感受:
1e10啊!!!!第一次做的时候不管三七二十一就最简单的find函数去上交,竟然A了。
想想这个复杂度不对啊,查题解各种涨姿势ORZ ORZ ORZ 当时现场赛的同学一定是崩溃的= =
具体代码如下:
并查集君:
#include<cstdio> #include<iostream> #define ll long long using namespace std; const int INF = 0x7f7f7f7f; const int MAXN = 1e5 + 111; int p[MAXN]; int qt[MAXN], qv[MAXN], mark[MAXN]; int t; int find(int x) { return mark[x] < t ? x : p[x] = find(p[x]); // 小于则说明在查询之前已经染过颜色 } int main() { int n, q; while (~scanf("%d%d", &n, &q) && (n | q)) { for (int i = 2; i <= n; ++i) { scanf("%d", p + i); mark[i] = INF; } int cnt = 0, x; char op[2]; for (int i = 1; i <= q; ++i) { scanf("%s%d", op, &x); if (op[0] == 'M') mark[x] = min(mark[x], i); // 记录最早染色时间 else { qt[cnt] = i; qv[cnt++] = x; } } ll ans = 0; while (cnt --) { t = qt[cnt]; // 查询发生的时间 ans += find(qv[cnt]); } printf("%lld\n", ans); } return 0; }
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <queue> #include <cstdlib> #include <algorithm> #include <stack> #include <map> #include <queue> #include <vector> using namespace std; const int maxn = 1e5+100; const int INF = 0x3f3f3f3f; #define pr(x) // cout << #x << " = " << x << " "; #define prln(x) // cout << #x << " = " << x <<endl; #define ll long long int head[maxn], nxt[maxn], to[maxn], dfsn, cnt, id[maxn], r[maxn], _n, sum[maxn<<2], dep[maxn]; void addedge(int u, int v) { nxt[cnt] = head[u]; head[u] = cnt; to[cnt++] = v; } void init(int n) { cnt = dfsn = 0; _n = 1; while(_n < n) _n = _n*2; int _nn = _n*2; for(int i = 0; i <= _nn; ++i) sum[i] = -1; for(int i = 0; i <= n;++i) { head[i] =-1; } } void dfstree(int fa,int u) { id[u] = ++dfsn; dep[u] = dep[fa]+1; for(int i = head[u]; ~i; i = nxt[i]){ dfstree(u,to[i]); } r[u] = dfsn; } inline void getans(int& ans, const int& v2){ if(ans == -1 || dep[ans] < dep[v2]) ans = v2; } void pushdown(int rt) { if(sum[rt] != -1) { getans(sum[rt<<1], sum[rt]); getans(sum[rt<<1|1],sum[rt]); } } void update(int rt, int l, int r, int ql, int qr, int v) { if(ql <= l && r <= qr) { getans(sum[rt], v); return; } pushdown(rt); int m = l + r >> 1; if(m >= ql) update(rt<<1, l, m, ql, qr, v); if(m < qr) update(rt<<1|1, m+1, r, ql, qr, v); } int query(int rt) { rt += _n-1; int ans = 0; while(rt>=1) { getans(ans,sum[rt]); rt = rt>>1; } return ans; } int main(){ #ifdef LOCAL freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin); //freopen("C:\\Users\\Administrator\\Desktop\\out.txt","w",stdout); #endif int n, m, x; char op[10]; while(cin >> n >> m && (n||m)) { ll ans = 0; init(n); for(int i = 2; i <= n; ++i) { scanf("%d", &x); addedge(x,i); } dep[0] = 0; dfstree(0,1); update(1, 1, _n, id[1], r[1], 1); for(int i = 0; i < m; ++i) { scanf("%s%d", op, &x); if(op[0] == 'M') update(1, 1, _n, id[x], r[x], x); else ans += query(id[x]); } printf("%lld\n", ans); } return 0; }