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\) 的可行条件为

\[\max_i f(i,mid)+\max_j f(j,mid) -f(lca,mid)\times 2-g(lca,mid)\ge 0 \]


发现直接存储 \(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;
}
posted @ 2026-03-24 12:34  TP2010  阅读(5)  评论(0)    收藏  举报