P7357 「PMOI-1」中位数 题解
前言
请注意,本题中的中位数的定义与数学中的定义略有不同:这里一个长度为 \(t\) 的序列的中位数定义为这个序列第 \(\left\lceil\frac{t+1}2\right\rceil\) 小的数。
首先自嘲一下我的无知,\(\lceil x \rceil\) 是上取整,\(\lfloor x \rfloor\) 是下取整。
解析
考虑快速求出一个无序数列的中位数。
令 \(g(x,y)=\begin{cases} -1 & (x<y) \\ 1 & (x\ge y) \end{cases}\),则发现中位数为最大的 \(x\) 满足 \(\sum g(a_i,x) \ge 0\)。
当 \(x\) 增大时,有些 \(g(a_i,x)\) 就会从 \(1\) 变为 \(-1\),则上述条件就越难满足,则具有单调性,考虑在值域上二分 \(mid\)。
接着,思考静态查询。
我们用二分转化为了可行性问题,则求 \(u\) 子树内一点 \(i\) 和 \(v\) 子树内一点 \(j\),使 \(i,j\) 路径上的点满足上述条件。
考虑树上差分,对于每个 \(x\) 维护每个点到根节点的链上的 \(g(a_u,x)\) 之和,记作 \(f(u,x)\)。
则记 \(lca\) 为 \(u,v\) 的最近公共祖先,询问 \(u,v\) 的可行条件为
发现直接存储 \(f\) 数组空间复杂度是 \(O(nA)\) 的,且也不好求出。
但发现 \(x-1\) 与 \(x\) 的 \(f\) 的差异非常小,因为只会有 \(a_u=x-1\) 的点的 \(g(a_u,x)\) 会从 \(1\) 变为 \(-1\),只会对 \(u\) 子树内的点 \(v\) 的 \(f(v,x)\) 造成影响,表现为一个区间加。
经过以上分析,由于不同 \(x\) 的差异很小考虑使用主席树来利用其结构,同时区间加使用标记永久化。
这里由于是做子树加减,所以以 dfs 作为下标,按值域顺序建主席树。
对于单点异或 \(1\) 直接分讨:
情况一:\(a_u\) 为奇数
则 \(a_u'=a_u-1\)。
对于 \(x\le a_u'\),\(g(a_u',x)=g(a_u,x)=1\),没有影响。
对于 \(x > a_u\),\(g(a_u',x)=g(a_u,x)=-1\),没有影响。
对于 \(x=a_u\),\(g(a_u,x)=1,g(a_u',x)=-1\),对 \(u\) 子树内的 \(f\) 区间减 2。
情况二:\(a_u\) 为偶数
则 \(a_u'=a_u+1\)。
对于 \(x\le a_u\),\(g(a_u',x)=g(a_u,x)=1\),没有影响。
对于 \(x > a_u'\),\(g(a_u',x)=g(a_u,x)=-1\),没有影响。
对于 \(x=a_u'\),\(g(a_u,x)=-1,g(a_u',x)=1\),对 \(u\) 子树内的 \(f\) 区间加 2。
则也可以转化为区间加减。
建树复杂度 \(O(n\log n)\)。
查询复杂度 \(O(\log^2 n)\)。
修改复杂度 \(O(\log n)\)。
很少见主席树加标记永久化,附代码。
#include <bits/stdc++.h>
using namespace std;
#define gc() (rp1==rp2&&(rp2=(rp1=buf)+fread(buf,1,1<<22,stdin))==rp1?EOF:*rp1++)
// #define gc() getchar()
char buf[1<<22],*rp1,*rp2;
inline int read(){
int d=0,f=0;char ch=gc();
while (!isdigit(ch)) f|=(ch=='-'),ch=gc();
while (isdigit(ch)) d=d*10+ch-'0',ch=gc();
return f?-d:d;
}
inline void write(int n){
int stk[30],tp=0;
do{stk[++tp]=n%10;n/=10;}while (n);
while (tp) putchar(stk[tp--]+'0');
putchar('\n');
}
const int N=100005,inf=1e9+7;
int n,q,a[N],b[N<<1],bcnt,c[N],h[N],idx,LG;
struct Edge{int t,ne;}e[N<<1];
inline void add(int a,int b) {e[idx]={b,h[a]};h[a]=idx++;}
int dep[N],sz[N],fa[20][N],dfn[N],from[N],tim;
void dfs(int u,int father){
from[dfn[u]=++tim]=u,fa[0][u]=father,dep[u]=dep[father]+1,sz[u]=1;
for (int i=1;(1<<i)<=dep[u];i++) fa[i][u]=fa[i-1][fa[i-1][u]];
for (int i=h[u];~i;i=e[i].ne) if (e[i].t!=father) dfs(e[i].t,u),sz[u]+=sz[e[i].t];
}
inline int LCA(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
for (int i=LG;i>=0;i--)
if (dep[x]-(1<<i)>=dep[y]) x=fa[i][x];
if (x==y) return x;
for (int i=LG;i>=0;i--)
if (fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
return fa[0][x];
}
vector <int> bu[N<<1];
struct Tree{int ls,rs,val,tag;}t[N*60];
int rt[N<<1],tot;
#define ls(u) (t[u].ls)
#define rs(u) (t[u].rs)
#define mid (l+r>>1)
inline void pushup(int u) {t[u].val=max(t[ls(u)].val,t[rs(u)].val)+t[u].tag;}
void build(int &u,int l=1,int r=n){
u=++tot;if (l==r) return t[u]={0,0,dep[from[l]],0},void();
build(ls(u),l,mid),build(rs(u),mid+1,r),pushup(u);
}
void updata(int &u,int v,int ql,int qr,int d,int l=1,int r=n){
t[u=++tot]=t[v];
if (ql<=l&&r<=qr) return t[u].val+=d,t[u].tag+=d,void();
if (ql<=mid) updata(ls(u),ls(v),ql,qr,d,l,mid);
if (qr>mid) updata(rs(u),rs(v),ql,qr,d,mid+1,r);
pushup(u);
}
int query(int u,int ql,int qr,int l=1,int r=n){
if (ql<=l&&r<=qr) return t[u].val;
int ans=-inf;
if (ql<=mid) ans=max(ans,query(ls(u),ql,qr,l,mid));
if (qr>mid) ans=max(ans,query(rs(u),ql,qr,mid+1,r));
return ans+t[u].tag;
}
int main(){
n=read(),q=read(),idx=0;LG=__lg(n)+1;
memset(h,-1,sizeof h);
for (int i=1;i<=n;i++) {
a[i]=read();b[++bcnt]=a[i];
if (a[i]^1) b[++bcnt]=a[i]^1;
}
for (int i=1,u,v;i<n;i++)
u=read(),v=read(),add(u,v),add(v,u);
dfs(1,0);
sort(b+1,b+bcnt+1),bcnt=unique(b+1,b+bcnt+1)-b-1;
for (int i=1;i<=n;i++)
c[i]=lower_bound(b+1,b+bcnt+1,a[i])-b,bu[c[i]].push_back(i),c[i]+=(a[i]&1)^1;
build(rt[0]);
for (int i=1;rt[i]=rt[i-1],i<=bcnt;i++)
for (int u:bu[i-1]) updata(rt[i],rt[i],dfn[u],dfn[u]+sz[u]-1,-2);
while (q--){
int op=read();
if (op==1){
int u=read(),x=c[u];
if (a[u]&1) updata(rt[x],rt[x],dfn[u],dfn[u]+sz[u]-1,-2);
else updata(rt[x],rt[x],dfn[u],dfn[u]+sz[u]-1,2);
a[u]^=1;
}
else {
int u=read(),v=read(),lca=LCA(u,v),l=0,r=bcnt,ans=1;
while (l<=r){
// printf("mid:%d\n",b[mid]);
int t1=query(rt[mid],dfn[u],dfn[u]+sz[u]-1);
int t2=query(rt[mid],dfn[v],dfn[v]+sz[v]-1);
int t3=query(rt[mid],dfn[lca],dfn[lca]);
int t4=(a[lca]>=b[mid])?1:-1;
bool flag=(t1+t2-t3*2+t4)>=0;
// printf("%d %d %d %d %d",t1,t2,t3,t4,t1+t2-t3*2+t4);
if (flag) ans=mid,l=mid+1;
else r=mid-1;
// puts("");
}
write(b[ans]);
}
}
return 0;
}

浙公网安备 33010602011771号