测试地址:网络管理
做法: 本题需要用到树状数组+主席树。
经典的带修改树上路径第
k
k
k大问题,不过我太菜了居然忘了有树上主席树这个东西…
不带修改的话能用树上主席树做,那么带修改怎么办呢?因为一次修改会影响一棵子树上的所有线段树,所以我们还是把树拍成DFS序,就变成区间修改了。注意到查询的时候只是查单棵线段树,因此可以运用差分的方法,把上述操作变成单点修改,前缀和求和,这个就用树状数组套主席树(准确上来讲算树状数组套动态开点线段树?)就可以统计修改的贡献了(即变成单次修改/查询
log
n
\log n
logn棵线段树)。注意原来点的贡献和修改的贡献是在不同的结构上统计的,一个是在树上主席树上统计,一个是在树状数组套线段树上统计。在二分的时候,把所有带贡献的线段树点列出来,每次算贡献的时候都扫一遍,向下走的时候所有点一起走即可。这样我们就以
O
(
n
log
2
n
)
O(n\log^2n)
O(nlog2n)的时间复杂度解决了这个问题。
写起来挺复杂,但也不是太难,居然1A了,信心获得增长。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,q,m,tot=0,val[100010],tote=0,first[100010]={0};
int pos[200010],k[100010],a[100010],b[100010];
int fa[100010][20]={0},dep[100010]={0},in[100010],out[100010],tim=0;
int seg[10000010]={0},ch[10000010][2]={0},rt[200010]={0};
int totp,p[510],type[510];
struct edge
{
int v,next;
}e[200010];
struct forsort
{
bool type;
int id,val;
}f[200010];
void insert(int a,int b)
{
e[++tote].v=b;
e[tote].next=first[a];
first[a]=tote;
}
void pushup(int v)
{
seg[v]=seg[ch[v][0]]+seg[ch[v][1]];
}
void insert(int &v,int last,int l,int r,int x)
{
v=++tot;
seg[v]=seg[last];
ch[v][0]=ch[last][0];
ch[v][1]=ch[last][1];
if (l==r) {seg[v]++;return;}
int mid=(l+r)>>1;
if (x<=mid) insert(ch[v][0],ch[last][0],l,mid,x);
else insert(ch[v][1],ch[last][1],mid+1,r,x);
pushup(v);
}
void modify(int &v,int l,int r,int x,int d)
{
if (!v) v=++tot;
if (l==r) {seg[v]+=d;return;}
int mid=(l+r)>>1;
if (x<=mid) modify(ch[v][0],l,mid,x,d);
else modify(ch[v][1],mid+1,r,x,d);
pushup(v);
}
void dfs(int v)
{
in[v]=++tim;
insert(rt[v],rt[fa[v][0]],1,m,val[v]);
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa[v][0])
{
fa[e[i].v][0]=v;
dep[e[i].v]=dep[v]+1;
dfs(e[i].v);
}
out[v]=tim;
}
int lowbit(int x)
{
return x&(-x);
}
void Add(int v,int x,int d)
{
for(int i=v;i<=n;i+=lowbit(i))
modify(rt[n+i],1,m,x,d);
}
void insert_p(int v,int d)
{
if (!v) return;
p[++totp]=v,type[totp]=d;
}
void Sum(int v,int d)
{
for(int i=v;i;i-=lowbit(i))
insert_p(rt[n+i],d);
}
void query(int k)
{
int l=1,r=m,s,mid;
while(l!=r)
{
s=0,mid=(l+r)>>1;
for(int i=1;i<=totp;i++)
s+=seg[ch[p[i]][1]]*type[i];
if (s<k)
{
r=mid;
for(int i=1;i<=totp;i++)
p[i]=ch[p[i]][0];
k-=s;
}
else
{
l=mid+1;
for(int i=1;i<=totp;i++)
p[i]=ch[p[i]][1];
}
}
s=0;
for(int i=1;i<=totp;i++)
s+=seg[p[i]]*type[i];
if (s<k) printf("invalid request!\n");
else printf("%d\n",pos[l]);
}
bool cmp(forsort a,forsort b)
{
return a.val<b.val;
}
int lca(int a,int b)
{
if (dep[a]<dep[b]) swap(a,b);
for(int i=18;i>=0;i--)
if (dep[fa[a][i]]>=dep[b])
a=fa[a][i];
if (a==b) return a;
for(int i=18;i>=0;i--)
if (fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&f[++tot].val);
f[tot].type=0,f[tot].id=i;
}
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
insert(a,b),insert(b,a);
}
for(int i=1;i<=q;i++)
{
scanf("%d%d%d",&k[i],&a[i],&b[i]);
if (!k[i])
{
f[++tot].type=1,f[tot].id=i;
f[tot].val=b[i];
}
}
sort(f+1,f+tot+1,cmp);
m=0;
for(int i=1;i<=tot;i++)
{
if (i==1||f[i].val!=f[i-1].val) pos[++m]=f[i].val;
if (f[i].type) b[f[i].id]=m;
else val[f[i].id]=m;
}
tot=0;
dep[1]=1;
dfs(1);
for(int i=1;i<=18;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
for(int i=1;i<=q;i++)
{
if (!k[i])
{
Add(in[a[i]],val[a[i]],-1);
Add(out[a[i]]+1,val[a[i]],1);
val[a[i]]=b[i];
Add(in[a[i]],val[a[i]],1);
Add(out[a[i]]+1,val[a[i]],-1);
}
else
{
totp=0;
insert_p(rt[a[i]],1);
insert_p(rt[b[i]],1);
int g=lca(a[i],b[i]);
insert_p(rt[g],-1);
if (fa[g][0]) insert_p(rt[fa[g][0]],-1);
Sum(in[a[i]],1),Sum(in[b[i]],1),Sum(in[g],-1);
if (fa[g][0]) Sum(in[fa[g][0]],-1);
query(k[i]);
}
}
return 0;
}