洛谷P2617 Dynamic Rankings 题解

Jun 17th, 2020
  • 在其它设备中阅读本文章

这是一道练习树套树的好题, 只不过窝不会 w.

所以开始考虑用分块乱搞. 首先对序列分块, 然后对权值分块. 维护 $sma_{i,j}$ 表示 序列 前 $i$ 块中的数, 在第 $j$ 块中的数的个数, $smb_{i,j}$ 表示 序列 前 $i$ 块中的数, 为 $j$ 的数的个数, 之后的询问和修改操作都围绕这两个块来进行.

对于一个区间 $[L,R]$ 的询问, 如果 $[L,R]$ 在同一块内暴力做. 否则, 先暴力的处理两个数组 $smc_{i},smd_{i}$ 分别表示在两侧不完整的块中 在第 $i$ 块中的数的个数和 为 $i$ 的数的个数. 暴力枚举每一个 值域 分成的块, 可以利用 $sma$ 和 $smc$ , $\Theta(1)$ 地求出区间内范围在这一块中数的个数, 确定答案在哪一块中. 再暴力枚举那一块中每一个数, 确定最终答案.

Code

// a[i]:第i个位置上的数(已离散化) b[i]:序列第i个位置所在块编号 c[i]:i这个数所在块编号
// s:序列分块的块大小 ss:值域分块的块大小
x=ri[i]/*L*/; y=rj[i]/*R*/; z=rk[i]/*k*/; f=true;
if (b[x]==b[y]) {/*同一块中暴力做*/}
else {
    for (int j=(b[x]+1)*s;j>=x;--j) {++smc[c[a[j]]]; ++smd[a[j]];}
    for (int j=b[y]*s+1;j<=y;++j) {++smc[c[a[j]]]; ++smd[a[j]];} // 处理出smc和smd
    for (int j=0;j<=ss&&f;++j) /*枚举值域块*/ {
        if (z<=(r=sma[b[y]-1][j]-sma[b[x]][j]+smc[j])) /*如果在这一块内*/
            for (int k=j*ss+1;;++k) /*枚举块内的每一个值*/ {
                if (z<=(r=smb[b[y]-1][k]-smb[b[x]][k]+smd[k])) {
                    print(d[k]); f=false; break;
                } z-=r;
            } z-=r;
    }
    for (int j=(b[x]+1)*s;j>=x;--j) {--smc[c[a[j]]]; --smd[a[j]];}
    for (int j=b[y]*s+1;j<=y;++j) {--smc[c[a[j]]]; --smd[a[j]];} // 清空smc和smd,不能用memset,否则复杂度就变成了O(n)
}

对于一个修改操作, 只需要暴力地修改之后每一个块对应的前缀和即可.

x=ri[i]/*修改位置*/; y=rj[i]/*修改目标值*/;
for (int j=b[x];j<=s;++j) {
    --sma[j][c[a[x]]]; ++sma[j][c[y]];
    --smb[j][a[x]]; ++smb[j][y];
} a[x]=y;

修改和更新复杂度都是 $\Theta(n \sqrt{n})$ ($n,m$ 同级), 离散化一下即可过 $50%$ 的点. 代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#include <algorithm>
#include <immintrin.h>
#include <emmintrin.h>
#include <cmath>
#include <cstdio>
using namespace std;

int n,m,s,ss,ms,a[100005],b[100005],c[200005],d[200005];
int sma[325][455],smb[325][200005],smc[455],smd[200005];
int ri[100005],rj[100005],rk[100005];
bool rf[100005];

#define gc() (p0==p1&&(p1=(p0=buf0)+fread(buf0,1,16777216,stdin),p0==p1)?EOF:*p0++)
char buf0[16777217],*p0,*p1;
inline int read() {
    int ret=0; char ch=gc(); while (ch<48||ch>57) ch=gc();
    while (ch>47&&ch<58) {ret=(ret<<3)+(ret<<1)+(ch^48); ch=gc();}
    return ret;
}

char buf1[16777217],stk[23]; int p2,p3 = -1;
inline void flush() {
    fwrite(buf1,1,p3+1,stdout), p3 = -1;
}
inline void print(int x)
{
    if (p3>8388608) flush();
    do stk[++p2]=x%10+48; while (x/=10);
    do buf1[++p3]=stk[p2]; while (--p2);
    buf1[++p3]='\n';
}

int main() {
    int x,y,z,r; char ch; bool f;
    n=ms=read(); m=read();
    for (int i=1;i<=n;++i) a[i]=d[i]=read();
    for (int i=0;i<m;++i) {
        do ch=gc(); while (ch!=67&&ch!=81);
        if (ch==67) {ri[i]=read(); rj[i]=d[++ms]=read(); rf[i]=true;}
        else {ri[i]=read(); rj[i]=read(); rk[i]=read();}
    }
    sort(d+1,d+ms+1);
    for (int i=1;i<=n;++i) a[i]=lower_bound(d+1,d+ms+1,a[i])-d;
    for (int i=0;i<m;++i) if (rf[i]) rj[i]=lower_bound(d+1,d+ms+1,rj[i])-d;
    ss=(int)sqrt(ms); for (int i=1;i<=s;++i) c[i]=(i-1)/ss;
    s=(int)sqrt(n); for (int i=1;i<=n;++i) b[i]=(i-1)/s;
    for (int i=1;i<=n;++i) {
        ++sma[b[i]][c[a[i]]];
        ++smb[b[i]][a[i]];
    }
    for (int i=1;i<=s;++i) {
        for (int j=0;j<=ss;++j) sma[i][j]+=sma[i-1][j];
        for (int j=0;j<=ms;++j) smb[i][j]+=smb[i-1][j];
    }
    for (int i=0;i<m;++i) {
        if (rf[i]) {
            x=ri[i]; y=rj[i];
            for (int j=b[x];j<=s;++j) {
                --sma[j][c[a[x]]]; ++sma[j][c[y]];
                --smb[j][a[x]]; ++smb[j][y];
            } a[x]=y;
        }
        else {
            x=ri[i]; y=rj[i]; z=rk[i]; f=true;
            if (b[x]==b[y]) {
                for (int j=x;j<=y;++j) {++smc[c[a[j]]]; ++smd[a[j]];}
                for (int j=0;j<=ss&&f;++j) {
                    if (z<=smc[j])
                        for (int k=j*ss+1;;++k) {
                            if (z<=smd[k]) {
                                print(d[k]); f=false; break;
                            } z-=smd[k];
                        } z-=smc[j];
                }
                for (int j=x;j<=y;++j) {--smc[c[a[j]]]; --smd[a[j]];}
            }
            else {
                for (int j=(b[x]+1)*s;j>=x;--j) {++smc[c[a[j]]]; ++smd[a[j]];}
                for (int j=b[y]*s+1;j<=y;++j) {++smc[c[a[j]]]; ++smd[a[j]];}
                for (int j=0;j<=ss&&f;++j) {
                    if (z<=(r=sma[b[y]-1][j]-sma[b[x]][j]+smc[j]))
                        for (int k=j*ss+1;;++k) {
                            if (z<=(r=smb[b[y]-1][k]-smb[b[x]][k]+smd[k])) {
                                print(d[k]); f=false; break;
                            } z-=r;
                        } z-=r;
                }
                for (int j=(b[x]+1)*s;j>=x;--j) {--smc[c[a[j]]]; --smd[a[j]];}
                for (int j=b[y]*s+1;j<=y;++j) {--smc[c[a[j]]]; --smd[a[j]];}
            }
        }
    } flush(); return 0;
}

owo

mo-ha