jzoj3797 [NOIP2014模拟8.21] 签到题3

Description


题目极不友好
给定一棵有根树(根节点为1),每个点都带有权值,对于点u,其权值设为a[u],其父亲为fa[i]。现有两个函数f1,f2,定义如下:
如果u=1,f1[u]=a[u],f2[u]=1
否则
如果f1[fa[u]]+1 < a[u] f1[u]=a[u],f2[u]=1;
如果f1[fa[u]]+1 > a[u] f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]
如果f1[fa[u]]+1=a[u] f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]+1
现在有两种操作:
0 u val 表示将a[u]改成val
1 u 表示查询f1[u]和f2[u]

对于40%的数据 n<=1000,Q<=1000
对于另外40%的数据 保证这棵树为一条链
对于100%的数据 n<=200000,Q<=200000

Solution


可以发现节点x只会被x到root一条链上的节点影响,f1就是a[x]-dep[x]的最大值,f2则是满足f1的数量。那么树链剖分记录区间最大值和出现次数即可。早上十分zz地写了树状数组套n个set来找各种数字,考完被wife提醒只需要记录最大的就行了。。

可以用bfs求树链剖分所需的dfs序,考虑到每个相邻的同层节点一定相隔了子树size个,那么统计一下就ok

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef std:: pair <int, int> pair;
const int N=800005;
const int E=800005;

struct edge{int x,y,next;}e[E];
int size[N],dep[N],pos[N],fa[N],bl[N];
int max[N*4+1],tot[N*4+1];
int queue[N],mx[N];
int a[N],n;
int ls[N],edCnt;
bool vis[N];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void addEdge(int x,int y) {
    e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}

void bfs(int root) {
    int head=1,tail=1; vis[1]=1;
    queue[head]=root;
    while (head<=tail) {
        int now=queue[head++];
        for (int i=ls[now];i;i=e[i].next) {
            if (vis[e[i].y]) continue;
            vis[e[i].y]=1;
            dep[e[i].y]=dep[now]+1;
            fa[e[i].y]=now;
            queue[++tail]=e[i].y;
        }
    }
    drp(ti,n,1) {
        int now=queue[ti];
        size[now]=1;
        for (int i=ls[now];i;i=e[i].next) {
            if (e[i].y==fa[now]) continue;
            size[now]+=size[e[i].y];
            if (size[e[i].y]>size[mx[now]]) mx[now]=e[i].y;
        }
    }
    bl[1]=1;
    rep(ti,1,n) {
        int now=queue[ti];
        for (int i=ls[now];i;i=e[i].next) {
            if (e[i].y==fa[now]) continue;
            if (mx[now]==e[i].y) {
                bl[e[i].y]=bl[now];
            } else bl[e[i].y]=e[i].y;
        }
    }
    fill(vis,0);
    head=1,tail=1; vis[1]=vis[0]=1;
    queue[head]=1;
    while (head<=tail) {
        int now=queue[head++];
        if (!vis[mx[now]]) {
            vis[mx[now]]=1;
            queue[++tail]=mx[now];
        }
        for (int i=ls[now];i;i=e[i].next) {
            if (e[i].y==fa[now]||vis[e[i].y]) continue;
            vis[e[i].y]=1;
            queue[++tail]=e[i].y;
        }
    }
    int last=1; pos[1]=1;
    rep(i,2,n) {
        if (fa[queue[i]]!=fa[queue[i-1]]) last=pos[fa[queue[i]]];
        else last+=size[queue[i-1]];
        pos[queue[i]]=last+1;
    }
}

void modify(int now,int tl,int tr,int x,int v) {
    if (tl==tr) {
        max[now]=v;
        tot[now]=1;
        return ;
    }
    int mid=(tl+tr)>>1;
    if (x<=mid) modify(now<<1,tl,mid,x,v);
    else if (x>mid) modify(now<<1|1,mid+1,tr,x,v);
    max[now]=std:: max(max[now<<1],max[now<<1|1]);
    tot[now]=tot[now<<1|1];
    if (max[now<<1]==max[now<<1|1]) {
        tot[now]=tot[now<<1]+tot[now<<1|1];
    } else if (max[now<<1]>max[now<<1|1]) tot[now]=tot[now<<1];
}

pair query(int now,int tl,int tr,int l,int r) {
    if (tl==l&&tr==r) return pair(max[now],tot[now]);
    int mid=(tl+tr)>>1;
    if (r<=mid) return query(now<<1,tl,mid,l,r);
    else if (l>mid) return query(now<<1|1,mid+1,tr,l,r);
    else {
        pair qx=query(now<<1,tl,mid,l,mid);
        pair qy=query(now<<1|1,mid+1,tr,mid+1,r);
        if (qx.first>qy.first) return qx;
        else if (qx.first==qy.first) return pair(qx.first,qx.second+qy.second);
        else return qy;
    }
}

void get_ans(int x) {
    int tmp=x;
    pair ret=pair(0,0);
    while (x!=0) {
        pair qx=query(1,1,n,pos[bl[x]],pos[x]);
        if (qx.first>ret.first) {
            ret=qx;
        } else if (qx.first==ret.first) ret.second+=qx.second;
        x=fa[bl[x]];
    }
    printf("%d %d\n", ret.first+dep[tmp], ret.second);
}

int main(void) {
    n=read();
    rep(i,1,n) a[i]=read();
    rep(i,2,n) {
        int x=read(),y=read();
        addEdge(x,y);
    }
    bfs(1);
    rep(i,1,n) modify(1,1,n,pos[i],a[i]-dep[i]);
    int q=read();
    while (q--) {
        int opt=read(),x=read();
        if (opt==0) {
            a[x]=read();
            modify(1,1,n,pos[x],a[x]-dep[x]);
        } else if (opt==1) {
            get_ans(x);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值