描述
给定一棵有根树(根节点为 111),每个点都带有权值,对于点uuu,其权值设为 a[u]a[u]a[u],其父亲为 fa[i]fa[i]fa[i]。现有两个函数 f1,f2f1,f2f1,f2,定义如下:
如果 u=1u=1u=1,f1[u]=a[u],f2[u]=1f1[u]=a[u],f2[u]=1f1[u]=a[u],f2[u]=1
否则
如果f1[fa[u]]+1<a[u]f1[fa[u]]+1<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>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]]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−1n−1行, 每行两个整数u,vu,vu,v,代表uuu与vvv 连有一条边。
接下来一行为一个正整数 QQQ。
下面 QQQ行,每一 行格式为0,u,val0,u,val0,u,val或 1,u1,u1,u。
输出
对于每种格式为 1 u 的询问,输出一行两个数,分别为f1[u],f2[u]
样例输入 [复制]
3
1 2 3
1 2
2 3
1
1 3
样例输出 [复制]
3 3
提示
对于 40%的数据 n<=1000,Q<=1000n<=1000,Q<=1000n<=1000,Q<=1000
对于另外 40%的数据 保证这棵树为一条链
对于 100%的数据 n<=200000,Q<=200000n<=200000,Q<=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
*/