1 条题解

  • 0
    @ 2025-8-24 22:29:06

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Y_B_X
    ...

    搬运于2025-08-24 22:29:06,当前版本为作者最后更新于2021-10-03 16:20:15,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目链接

    我不会告诉你我是因为题目背景的头像与我们班主任一毛一样才来写题解的

    题意:
    给出一棵树,点有点权,每次将一个点的点权异或 11
    或给出两个点 u,vu,v ,询问能覆盖 u,vu,v 的路径所形成的序列的中位数最大值。
    保证这 u,vu,v 不在一条链上,这里中位数指长为 nn 的序列中第 n+12\left\lfloor\frac{n+1}{2}\right\rfloor 大的数。

    Step 1\text{Step 1}

    先看一下这种定义下的中位数的性质:

    对于一个序列 S,n=SS,n=\left|S\right|

    设 $g_a=|\{i|s_i\geq a,1\leq i\leq n\}|,l_a=|\{i|s_i< a,1\leq i\leq n\}|$

    SS 的中位数就是原序列中满足 galag_a\leq l_a 的最大的 aa

    如果把 a\geq a 的数设为 11 ,把 <a<a 的数设为 1-1aa 是中位数时必须这些 111-1 的和 0\geq 0 ,且 aa 最大。

    由于设成 111-1 的操作在 aa 递增时 1-1 增多,可以建出一颗主席树来维护。

    这套路在 此题 中曾出现过。

    Step 2\text{Step 2}

    对于题目中的满足覆盖 u,vu,v 的路径的中位数最大值,是有单调性的,考虑二分。

    对于一个二分的值 midmid 我们希望得知:把树上每个点权 mid\geq mid 的点的 键值 设成 11 ,把点权 <mid<mid 的点的键值 设成 1-1 后,查看能覆盖 u,vu,v 路径中 键值的和的最大值 能否 0\geq 0

    由于要覆盖 u,vu,vu,vu,v 不在一条路径上,考虑树上差分。

    fxa=a时x到根路径键值之和f_x^a=\text{a时x到根路径键值之和}

    所以二分时要求的键值的和的最大值就是:

    $\max\limits_{x\in \operatorname{subtree}(u)}f_x^{mid}+\max\limits_{y\in \operatorname{subtree}(v)}f_y^{mid}-2f_{\operatorname{lca}(u,v)}^{mid}+w_{\operatorname{lca}(u,v)}^{mid}$

    其中 wxaw_x^{a} 代表 aaxx 的键值。

    于是希望对每个 aa 维护 dfsdfs 序所代表的点的 fxaf_x^a 值,这样就容易查询子树最大值。

    Step 3\text{Step 3}

    考虑如何用主席树维护 fxaf_x^a 值。

    对于 a=0a=0 时,fxa=dep(x)f_x^a=\operatorname{dep}(x) ,即 xx 的深度,因为所有点的点权 0\geq 0

    aa 变大时会导致一些点的键值从 11 变成 1-1

    那这些点的子树内的所有点的 faf^a 值就会 2-2

    由于主席树是以 dfsdfs 序为下标建的,这相当于区间减并可持久化。

    这可以用 标记永久化 轻松实现。

    Step 4\text{Step 4}

    现在只剩下最后一个问题:把点权异或 11

    开始建主席树时顺便把每个点的点权异或 11 后的值顺带着离散化并建主席树。

    由于异或 11 后一个点的点权要么变大一,要么减小一。

    tx=2x2+1t_x=2\left\lfloor\frac{x}{2}\right\rfloor+1 ,即不比 xx 小的最小的奇数。

    那么 a>txa>t_x 的版本无论如何都不发生改变,因为max(x,xxor1)tx\max(x,x\operatorname{xor}1)\leq t_x

    atx1a\leq t_x-1 的版本同样不发生变化,因为 min(x,xxor1)tx1\min(x,x\operatorname{xor}1)\geq t_x-1

    所以受改动的版本仅有 txt_x 一个。

    xx 为奇数, tx=xt_x=x , 异或后变小成 x1x-1

    那原先 xx 的版本中这个点的键值为 11 ,权值变小后要成为 1-1

    所以将 xx 的版本中这个点的子树内所有点的 ff2-2

    xx 为偶数, tx=x+1t_x=x+1 ,异或后变大成 x+1x+1

    那原先 x+1x+1 的版本中这个点的键值为 1-1 ,权值变大后要成为 11

    所以将 x+1x+1 的版本中这个点的子树内所有点的 ff+2+2

    这同样可用标记永久化实现。

    Step 5\text{Step 5}

    预处理时间复杂度为 O(nlogn)O(n\log n)

    更改点权的时间复杂度单次为 O(logn)O(\log n)

    查询要套个二分,单次 O(log2n)O(\log^2 n)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;
    int n,m,nn,x,y,opt,tot,tt,l,L,R,mid,cnt,qx,qy;
    int a[N],b[N],rt[N],ii[N];
    int dfn[N],rev[N],sz[N],dep[N],f[N][20];
    int to[N],nextn[N],h[N],edg;char ch;
    inline void add(int x,int y){to[++edg]=y,nextn[edg]=h[x],h[x]=edg;}
    #define id(x) lower_bound(b+1,b+nn+1,x)-b
    struct tree{int l,r,tag,mx;}t[N<<5];
    inline void read(int &x){
    	x=0;ch=getchar();while(ch<48||ch>57)ch=getchar();
    	while(ch>47&&ch<58)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    }
    void write(int x){if(x>9)write(x/10);putchar(48+x%10);}
    void build(int &k,int l,int r){
    	k=++tot;
    	if(l^r){
    		int mid=(l+r)>>1;
    		build(t[k].l,l,mid);build(t[k].r,mid+1,r);
    		t[k].mx=max(t[t[k].l].mx,t[t[k].r].mx);
    	}
    	else t[k].mx=dep[rev[l]];
    }
    void update(int &k,int kk,int l,int r,int x,int y,int v){
    	k=++tot;t[k]=t[kk];
    	if(x<=l&r<=y)t[k].tag+=v,t[k].mx+=v;
    	else {
    		int mid=(l+r)>>1;
    		if(x<=mid)update(t[k].l,t[kk].l,l,mid,x,y,v);
    		if(mid<y)update(t[k].r,t[kk].r,mid+1,r,x,y,v);
    		t[k].mx=max(t[t[k].l].mx,t[t[k].r].mx)+t[k].tag;
    	}
    }
    int inquiry(int k,int l,int r,int pos){
    	if(l^r){
    		int mid=(l+r)>>1;
    		if(pos<=mid)return inquiry(t[k].l,l,mid,pos)+t[k].tag;
    		else return inquiry(t[k].r,mid+1,r,pos)+t[k].tag;
    	}
    	else return t[k].mx;
    }
    int inquiry_mx(int k,int l,int r,int x,int y){
    	if(x<=l&&r<=y)return t[k].mx;
    	int mid=(l+r)>>1;
    	if(x<=mid&&mid<y)
    		return max(inquiry_mx(t[k].l,l,mid,x,y),inquiry_mx(t[k].r,mid+1,r,x,y))+t[k].tag;
    	else if(x<=mid)return inquiry_mx(t[k].l,l,mid,x,y)+t[k].tag;
    	else return inquiry_mx(t[k].r,mid+1,r,x,y)+t[k].tag;
    }
    void init(int x,int anc){
    	int i,y;rev[dfn[x]=++tt]=x;
    	sz[x]=1;dep[x]=dep[anc]+1;f[x][0]=anc;
    	for(i=1;i^20;++i)f[x][i]=f[f[x][i-1]][i-1];
    	for(i=h[x];y=to[i],i;i=nextn[i])if(y^anc)init(y,x),sz[x]+=sz[y];
    	h[x]=0;
    }
    int lca(int x,int y){
    	int i;if(dep[x]<dep[y])x^=y^=x^=y;
    	for(i=19;~i;--i)if(dep[f[x][i]]>=dep[y])x=f[x][i];
    	if(x==y)return x;
    	for(i=19;~i;--i)if(f[x][i]^f[y][i])x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    main(){
    	read(n);read(m);register int i,j;
    	for(i=1;i<=n;++i){read(a[i]),b[++nn]=a[i];if(a[i]^1)b[++nn]=a[i]^1;}
    	sort(b+1,b+nn+1);nn=unique(b+1,b+nn+1)-b-1;
    	for(i=1;i^n;++i)read(x),read(y),add(x,y),add(y,x);
    	init(1,0);edg=0;build(rt[0],1,n);
    	for(i=1;i<=n;++i)add(ii[i]=id(a[i]),i),ii[i]+=(a[i]&1)^1;
    	for(i=1;rt[i]=rt[i-1],i<=nn;++i)for(j=h[i-1];x=to[j],j;j=nextn[j])
    		update(rt[i],rt[i],1,n,dfn[x],dfn[x]+sz[x]-1,-2);
    	while(m--){
    		read(opt);read(x);
    		if(opt==1){
    			i=ii[x];
    			if(a[x]&1)update(rt[i],rt[i],1,n,dfn[x],dfn[x]+sz[x]-1,-2);
    			else update(rt[i],rt[i],1,n,dfn[x],dfn[x]+sz[x]-1,2);
    			a[x]^=1;
    		}
    		else {
    			read(y);l=lca(x,y);
    			L=0;R=nn;
    			while(L<R){
    				mid=L+R+1>>1;cnt=0;
    				cnt+=inquiry_mx(rt[mid],1,n,dfn[x],dfn[x]+sz[x]-1);
    				cnt+=inquiry_mx(rt[mid],1,n,dfn[y],dfn[y]+sz[y]-1);
    				cnt-=(inquiry(rt[mid],1,n,dfn[l])<<1)+(a[l]>=b[mid]?-1:1);
    				if(cnt>=0)L=mid;else R=mid-1;
    			}
    			write(b[L]);putchar('\n');
    		}
    	}
    }
    
    • 1

    信息

    ID
    6400
    时间
    3000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者