jzoj3797 签到题3

本文介绍了一道关于树剖结合线段树的问题解决方法。通过定义特定函数f1和f2,来求解从树的根节点到任意节点路径上的最大值及其出现次数。文章提供了完整的代码实现,并指出使用深度优先搜索(DFS)可能会导致栈溢出的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给定一棵有根树(根节点为1),每个点都带有权值,对于点u,其权值设为a[u],其父亲为fa[i]。现有两个函数f1,f2,定义如下:
如果u=1,f1[u]=a[u],f2[u]=1
否则
如果f1[fa[u]]+1

Input

第一行为n。第二行为n个正整数,第i个数代表第i个点初始时的权值a[i]。接下来n-1行,每行两个整数u,v,代表u与v连有一条边。接下来一行为一个正整数Q。下面Q行,每一行格式为0 u val 或1 u。

Output

对于每种格式为1 u的询问,输出一行两个数,分别为f1[u],f2[u]

Sample Input

3
1 2 3
1 2
2 3
1
1 3

Sample Output

3 3

Data Constraint

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

Solution

这道题确实很签到,因为实在是太好想了
题目给出的f1,f2函数不就是在求从根节点到某个结点的最大值以及最大值出现的次数吗
那么我们直接树剖再套上线段树就好了
表示对这道题的数据很不满意
一堆人打个水法 比我少一千bytes跑得还贼快
对了,用dfs会爆栈

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define L rt*2
#define R rt*2+1
using namespace std;
const int N=200005;
struct edge{
    int l,r,x,s,id;
} tr[4*N];
int x,y,z,n,i,qq,tot,fl,w1,w2,k,kk,yy,al;
int f1[N],f2[N],a[N],fa[N],he[N],nx[2*N],b[2*N],top[N],h[N],q[N],s[N],seq[N],dfn[N],son[N];
int read(){
    int sum=0;
    char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9'){
        sum=sum*10+c-'0';
        c=getchar();}
    return sum;
}
void add(int x,int y){nx[++tot]=he[x];he[x]=tot;b[tot]=y;}
inline void update(int rt){
    if (tr[L].x==tr[R].x) tr[rt].s=tr[L].s+tr[R].s,tr[rt].id=tr[L].id;
      else if (tr[L].x>tr[R].x) tr[rt].s=tr[L].s,tr[rt].id=tr[L].id;
        else tr[rt].s=tr[R].s,tr[rt].id=tr[R].id;
    tr[rt].x=max(tr[L].x,tr[R].x);
}
inline void make(int rt,int l,int r){
    tr[rt].l=l,tr[rt].r=r;
    if (l==r){
        tr[rt].x=a[seq[l]]-h[seq[l]];
        tr[rt].s=1;
        tr[rt].id=seq[l];
        return;
    }
    int mid=(l+r)>>1;
    make(L,l,mid);
    make(R,mid+1,r);
    update(rt);
}
inline void change(int rt,int x,int z){
    if (tr[rt].l==tr[rt].r){
        tr[rt].x+=z; return;}
    int mid=(tr[rt].l+tr[rt].r)>>1;
    if (x<=mid) change(L,x,z); else change(R,x,z);
    update(rt);
}
void query(int rt,int l,int r){
    if (tr[rt].l==l&&tr[rt].r==r){
        if (tr[rt].x>k) {
            k=tr[rt].x;
            kk=tr[rt].s;
            al=tr[rt].id;} 
        else if (tr[rt].x==k) kk+=tr[rt].s;
        return;
    }
    int mid=(tr[rt].l+tr[rt].r)>>1;
    if (r<=mid) query(L,l,r);
      else if (l>mid) query(R,l,r);
        else query(L,l,mid),query(R,mid+1,r);
}
void bfs(){
    int l,r,i,j,t,res;
    l=0,r=1,q[r]=1;
    while (l<r){
        x=q[++l];
        for(i=he[x];i;i=nx[i]){
            j=b[i];
            if (j==fa[x]) continue;
            fa[j]=x; h[j]=h[x]+1;
            q[++r]=j;}
    }
    fo(i,1,n) top[i]=i,s[i]=1;
    fa[1]=0;
    fd(i,n,1) s[fa[q[i]]]+=s[q[i]];
    dfn[1]=1; s[0]=0;
    fo(i,1,n) {
        x=q[i],t=0,res=dfn[x];
        for(j=he[x];j;j=nx[j]) if (s[b[j]]>s[t]&&b[j]!=fa[x]) t=b[j]; 
        if (t) son[x]=t,top[t]=top[x],dfn[t]=res+1,res+=s[t];
        for(j=he[x];j;j=nx[j]) 
          if (b[j]!=t&&b[j]!=fa[x]) dfn[b[j]]=res+1,res+=s[b[j]];}
    fo(i,1,n) seq[dfn[i]]=i;
}
int main(){
    freopen("3.in","r",stdin);
    freopen("1.out","w",stdout);
    n=read();
    fo(i,1,n) a[i]=read();
    fo(i,1,n-1) add(x=read(),y=read()),add(y,x);
    fa[1]=0,h[1]=1;
    bfs();
    make(1,1,n);
    qq=read();
        while (qq){
            qq--;
            x=read();
            if (x==1){
                y=read(); yy=y;
                k=-1234456789; kk=0; x=0; 
                while (y!=0){
                  query(1,dfn[top[y]],dfn[y]);
                  y=fa[top[y]];
                  }
                printf("%d %d\n",h[yy]+a[al]-h[al],kk);
            } else {
                y=read(),z=read();
                change(1,dfn[y],z-a[y]);
                a[y]=z;
            }
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值