签到题(8.2考试)

描述

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

如果 u=1u=1u=1f1[u]=a[u],f2[u]=1f1[u]=a[u],f2[u]=1f1[u]=a[u],f2[u]=1
否则
如果f1[fa[u]]+1&lt;a[u]f1[fa[u]]+1&lt;a[u]f1[fa[u]]+1<a[u]f1[u]=a[u],f2[u]=1;f1[u]=a[u],f2[u]=1;f1[u]=a[u],f2[u]=1;
如果f1[fa[u]]+1&gt;a[u]f1[fa[u]]+1&gt;a[u]f1[fa[u]]+1>a[u]f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]
如果f1[fa[u]]+1=a[u]f1[fa[u]]+1=a[u]f1[fa[u]]+1=a[u]f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]+1f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]+1f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]+1

现在有两种操作:

  • 0,u,val0,u,val0,u,val表示将 a[u]a[u]a[u]改成 valvalval
  • 1,u1,u1,u 表示查询 f1[u]f1[u]f1[u]f2[u]f2[u]f2[u]
输入

第一行为nnn
第二行为 nnn 个正整数,第 iii个数代表第 iii个点初始时的权值 a[i]a[i]a[i]
接下来 n−1n−1n1行, 每行两个整数u,vu,vu,v,代表uuuvvv 连有一条边。
接下来一行为一个正整数 QQQ
下面 QQQ行,每一 行格式为0,u,val0,u,val0,u,val1,u1,u1,u

输出

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

样例输入 [复制]
3
1 2 3
1 2
2 3
1
1 3
样例输出 [复制]
3 3
提示

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

解析:

考完后,对这道题留了一个心眼,根据历年来在考场上摸爬滚打的经验,题干莫名其妙给了一些式子,这赤裸裸的要搞事嘛
有位大佬考试时用的暴力深搜优化,然后AC了,作为一个蒟蒻,我也用的暴力优化,但终究是蒟蒻,只得了40分,但为啥我赛后原代码交上校内网站上也AC了?

言归正传,该题正解是树剖加线段树,根据所给的式子,可以认为是求从根节点到iii节点路径上所出现的最大权值以及该权值所出现的次数
所以,先要将f1f1f1变形为一个与深度无关的变量,则将每一个点所给的权值减去该节点的深度在记录在线段树中,每一次修改,计算修改前后节点权值的差量,将差量加入线段树中操作
最后询问时统计从1到n的最大值及其出现次数

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]];//x记录权值减深度,将f1变为一个与深度无关的量 
        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){//个人认为是从1到i节点访问,用mx记录并更新统计的最大值 
    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;//dfn[i]表示树上的i号节点在线段树中的位置编号 
    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];//找重儿子记录在t中 
        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;//seq[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; //k记录目前统计到的最大值 
                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;
            }
        }
}
code(水过去的代码)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+5;
int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-f;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=(s<<3)+(s<<1)+ch-'0';
		ch=getchar();
	}
	return s*f;
}
struct fjy{
	int nxt,v;
}e[N<<1];
int fir[N],w[N],f1[N],f2[N],fa[N];
int cnt,ui,vi,wi,n,jud,t;
void add(int u,int v){
	e[++cnt].v=v;e[cnt].nxt=fir[u],fir[u]=cnt;
}
void dfs(int u,int father){
	fa[u]=father;
	if(f1[fa[u]]+1<w[u]) f1[u]=w[u],f2[u]=1;
	else if(f1[fa[u]]+1>w[u]) f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]];
	else if(f1[fa[u]]+1==w[u]) f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]+1;
	for(int i=fir[u];i;i=e[i].nxt){
		if(e[i].v==father) continue;
		dfs(e[i].v,u);
	}
} 
void update(int u){
	if(f1[fa[u]]+1<w[u]) if(f1[u]==w[u]&&f2[u]==1)return;else f1[u]=w[u],f2[u]=1;
	else if(f1[fa[u]]+1>w[u]) if(f1[u]==f1[fa[u]]+1&&f2[u]==f2[fa[u]])return;else f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]];
	else if(f1[fa[u]]+1==w[u]) if(f1[u]==f1[fa[u]]+1&&f2[u]==f2[fa[u]]+1)return;else f1[u]=f1[fa[u]]+1,f2[u]=f2[fa[u]]+1;
	for(int i=fir[u];i;i=e[i].nxt) update(e[i].v);
}
signed main(){
//	freopen("checkin.in","r",stdin);
//	freopen("checkin.out","w",stdout);	
	n=read();
	for(int i=1;i<=n;i++)
	w[i]=read();
	for(int i=1;i<n;i++){
		ui=read(),vi=read();
		add(ui,vi);add(vi,ui);
	}
	f1[0]=f2[0]=-N;
	dfs(1,0);
	t=read();
	for(int i=1;i<=t;i++){
		jud=read();ui=read();
		if(jud==0){
			w[ui]=read();
			update(ui);
		}
		if(jud==1)printf("%d %d\n",f1[ui],f2[ui]);
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}
/*
5
3 0 9 2 1
1 4
3 2
4 5
5 2
12
1 1
1 4
1 5
1 2
1 3
0 5 10
1 5
1 2
1 3
0 2 11
1 2
1 3
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值