Aizu - 2170 Marked Ancestor

题意:给出一颗树,有两种操作:

1. mark  u  标记结点u

2.query  u  询问离u最近的且被标记的祖先结点是哪个

让你输出所有询问的和。

显然一个暴力的做法是每次都直接修改,然后查询的话就一个一个地向祖先查询,直到一个被标记过的点.

让我们来优化一下这个暴力.

类似于一个题https://www.luogu.com.cn/problem/P1653

考虑反着搞,如果一个点的最早被标记时间比当前时间大或者根本没有被标记,那么这个点以后都不会用到了,可以被路径压缩掉.

#include<cmath>
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<set>
#include<vector>
#include<bitset>
using namespace std;
const int maxn=100005;
int vis[maxn],fa[maxn],n,Q,cnt;
long long ans;
struct query
{
    int t,id;
}q[maxn];
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline void write(long long a)
{
    if(a<0)
    {
        char a='-',b='1';
        putchar(a);
        putchar(b);
    }
    else
    {
        if(a>=10)
            write(a/10);
        putchar(a%10+'0');
    }
}
int find(int id,int t)
{
    return vis[id]<t?id:fa[id]=find(fa[id],t);
}
int main()
{
    while(scanf("%d%d",&n,&Q)==2&&n&&Q)
    {
        for(int i=2;i<=n;++i)
            fa[i]=read(),vis[i]=1e9;
        ans=cnt=0;
        for(int i=1;i<=Q;++i)
        {
            char ch;
            cin>>ch;
            int x=read();
            if(ch=='Q')
                q[++cnt].t=i,q[cnt].id=x;
            else
                vis[x]=min(vis[x],i);
        }
        for(int i=cnt;i>=1;--i)
            ans+=find(q[i].id,q[i].t);
        write(ans);
        putchar('\n');
    }
    return 0;
}

 

当然,这仍然是个暴力(虽然在Aizu上面过了),比如树退化成一条链且每一次都查询叶子节点而不修改,就可以被卡到n方.

 

这里有一篇线段树维护的,大家可以看一下.

https://www.cnblogs.com/zeroxf/p/6749091.html

posted @ 2019-12-09 18:44  520Enterprise  阅读(224)  评论(1编辑  收藏  举报