Marked Ancestor
一道并查集的题目硬是被我当成线段树写了,感觉这样写虽然不是最好的,不过能a就行
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=103906#problem/G
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2170
首先题意:
一个可以被标记的树,起初只有根节点被标记了,然后有两种操作,第一种操作是把某一个点标记,第二个操作是询问他的祖先中离他最近的节点编号,最后出书询问的节点编号和.
这是我在并查集专题上面遇到的,不过第一反应暴力肯定不行(然而暴力的并查集查找祖先也可以过...),然后想半天没思路,因为当前节点的询问过后不能直接路径压缩,因为这样后面如果该条路径有一个点被标记那就gg了
但此时想法是如果这个点被标记了,那么他的后代可能都会受到影响,但是又不能一个一个去更新影响,想到这里再加上题目中的两种操作更新查询和线段树神似,所以就开始搞线段树,但是这里有一个问题:线段树更新的点都在一起...,接着想到以前一道没做出来的题(翻硬币类似的)用了dfsn重建树然后再更新维护,所以就用先序遍历了一下,同时我记录下了每个节点的深度,因为更新的时候,深度更深的点应该把深度浅的点信息更新掉(为啥?举一个抽象例子,如果点A被B和C更新了,那么肯定B和C是A的祖先,如果depB<depC,那么depC离A更近)
其他的都是细节问题了
#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; } } //dfs建树
//这里保证了一个点的后代都在一起被访问到,最左边的点是这个点id[u]表示最左边点//的编号,r[u]表示u后代最右边点编号 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; }
//这里是线段树的更新操作
//sum初始化为-1,如果当前未被更新或者更新的点深度小于v2这个点深度,,那么更新 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; }