[BZOJ4551][JZOJ4604]【TJOI&HEOI2016】D1T1 树

Description

在2016年,佳媛姐姐刚刚学习了树,非常开心。
现在他想解决这样一个问题:
给定一颗有根树(根为1),有以下两种操作:
1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)
2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖
先)你能帮帮他吗?

Solution

这题至少有两种解法。

  • 解法1:可以离线处理。把每个操作读入后倒序,就变成了删除节点,也就是合并联通块。这样完全可以用并查集完成。
  • 解法2:如果一定要在线的话。可以用树链剖分,每个询问就是要求 1 ~x的路径上最深的有标记的点。显然这里DFS序越大深度越大。修改就是把这个点改成它的DFS序,线段树维护最大值即可。

解法1的复杂度是 O(N+qα(N)) ,码量个人认为1K左右
解法2的复杂度是 O(Nlog2N) ,码量2.7K~~

然而比赛的时候,本人对自己的代码能力十分自信,机(sha)智(bi)的打了解法2。
结果一直拍到11:00

Code

下面是比赛时候打的链剖~

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define MAXN 100005
using namespace std;
struct note
{
    int mx,ls,rs;   
}tree[2000005];
struct rd
{
    int x,y;
}a[MAXN];
int top[MAXN],size[MAXN],son[MAXN],dfn[MAXN],pt[MAXN],ft[MAXN],deep[MAXN],n,m,a1[MAXN][2],df,num;
bool cmp(rd x,rd y)
{
    return x.x<y.x;
}
void dfs1(int k)
{
    int i,mx=0;
    if (a1[k][0]!=0)
    fo(i,a1[k][0],a1[k][1])
    {
        dfs1(a[i].y);
        size[k]+=size[a[i].y];
        if (size[a[i].y]>mx) 
        {
            son[k]=a[i].y;
            mx=size[a[i].y];
        }
    }
    size[k]++;
}
void dfs2(int k)
{
    int i;
    dfn[k]=++df;
    pt[df]=k;
    if (son[k]!=0)
    {
        top[son[k]]=top[k];
        dfs2(son[k]);
    }
    if (a1[k][0]!=0) 
    fo(i,a1[k][0],a1[k][1])
    {
        if(a[i].y==son[k]) continue;
        top[a[i].y]=a[i].y;
        dfs2(a[i].y);
    }
}
int find(int now,int l,int r,int x,int y)
{
    if (now==0) return 0;
    int ls=tree[now].ls,rs=tree[now].rs;
    if (l==x&&r==y) return tree[now].mx; 
    int mid=(l+r)/2;
    if (y<=mid) return find(ls,l,mid,x,y);
    else if(x>mid) return find(rs,mid+1,r,x,y);
    else return max(find(ls,l,mid,x,mid),find(rs,mid+1,r,mid+1,y));
}
void change(int now,int l,int r,int x,int y,int v)
{
    int ls=tree[now].ls,rs=tree[now].rs;
    if (l==x&&r==y) 
    {
        tree[now].mx=v;
        return;
    }
    int mid=(l+r)/2; 
    if (ls==0) ls=tree[now].ls=++num,tree[ls].mx=0;
    if (rs==0) rs=tree[now].rs=++num,tree[rs].mx=0;
    if (y<=mid) change(ls,l,mid,x,y,v); 
    else if(x>=mid) change(rs,mid+1,r,x,y,v);
    else change(ls,l,mid,x,mid,v),change(rs,mid+1,r,mid+1,y,v);
    tree[now].mx=max(tree[ls].mx,tree[rs].mx);
}
int ask(int x,int y)
{
    int fx=top[x],fy=top[y];
    if (fx==fy) 
    {
        if (deep[x]>deep[y]) swap(x,y);
        return find(1,1,n,dfn[x],dfn[y]);
    }
    else 
    {
        if (deep[fx]<deep[fy]) swap(fx,fy),swap(x,y);
        return max(find(1,1,n,dfn[fx],dfn[x]),ask(ft[fx],y));
    }
}
int main()
{
    cin>>n>>m;
    int i,j,k;
    deep[1]=1;
    memset(size,0,sizeof(size));
    df=0;
    fo(i,1,n-1) 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        ft[y]=x;
        deep[y]=deep[x]+1;
        a[i].x=x;
        a[i].y=y;
    }
    sort(a+1,a+n,cmp);
    fo(i,1,n-1)
    {
        if (a[i].x!=a[i-1].x)
        {
            a1[a[i].x][0]=i;
            a1[a[i-1].x][1]=i-1;
        } 
    }
    a1[a[n-1].x][1]=n-1;
    top[1]=1;
    dfs1(1);
    dfs2(1);
    num=1;
    change(1,1,n,1,1,1); 
    int pq=0;
    fo(i,1,m)
    {
        char ch;
        scanf("\n");
        scanf("%c%d",&ch,&k);
        if (ch=='Q') printf("%d\n",pt[ask(1,k)]);
        else change(1,1,n,dfn[k],dfn[k],dfn[k]); 
    } 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值