[AOJ 2170]Marked Ancestor[并查集][离线][路径压缩]or[线段树]

来源:互联网 发布:淘宝企业店铺开店资料 编辑:程序博客网 时间:2024/05/01 16:41
题目链接:[AOJ 2170]Marked Ancestor[并查集][离线][路径压缩]or[线段树]

题意分析:

结点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 longusing 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 longint 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);    elseans += query(id[x]);    }    printf("%lld\n", ans);    }    return 0;}


0 0
原创粉丝点击