BZOJ 2989 数列

目录

\(\frak Description\)

\(\rm Link.\)

\(\frak Solution\)

对于每个 \(x\),考虑满足 \(\text{graze}(x,i)\le k\)\(i\) 满足什么条件:容易想到将每个数搞成一个点对 \((i,a_i)\),这样 \(\text{graze}(x,i)\) 其实就是曼哈顿距离,那么合法的点 \(i\) 显然围绕点 \(x\) 形成一个正方形,只不过是旋转了 \(45^\circ\). 问题就转化成统计在这个正方形的点的数量。

我们可以用切比雪夫距离来再转化一下,点就变成了 \((a_i+i,a_i-i)\),但同时我们又将正方形转回来了。如果想看具体推导可以看看 \(\rm details\)

对于曼哈顿距离 \(D\) 有:

\[\begin{align} D&=|x_1-x_2|+|y_1-y_2|\\ &=\max\{x_1-x_2+y_1-y_2,x_2-x_1+y_1-y_2,x_1-x_2+y_2-y_1,x_2-x_1+y_2-y_1\}\\ &=\max\{|(x_2+y_2)-(x_1+y_1)|,|(x_2-y_2)-(x_1-y_1)|\}\\ &=\max\{|(x_2+y_2)-(x_1+y_1)|,|(y_1-x_1)-(y_2-x_2)|\} \end{align} \]

所以将点坐标转化成 \((x+y,x-y)\) 或者 \((y+x,y-x)\) 都可以用切比雪夫距离表示原来的 \(D\).

如果没有强制在线,现在就可以用 \(\rm cdq\) 分治 + 扫描线做到 \(\mathcal O(n\log^2 n)\)(以下均设 \(n\) 与值域同阶). 不过如果强制在线呢?这儿有个 并不船新\(\rm trick\):二进制分组!

二进制分组会把修改分为若干大小为 \(2\) 的幂次的组,每组用一个数据结构维护。新加入一个修改时,会根据修改个数和前面的修改进行合并(这个合并类似于二进制的进位,即,两个大小为 \(2\) 的组合并成大小为 \(4\) 的组,是不是很像 \(1024\) )。查询时在每一组的数据结构里分别查询。

这有啥用呢?有了它,我们就可以对于每一组维护一棵主席树,合并的时候暴力重构即可。

询问复杂度是 \(\mathcal O(q\log^2 n)\) 的,现在我们来看看合并复杂度:假设这是第 \(i\) 次修改,那么新加入的点就会和之前约 \(\text{lowbit}(i)\) 个点合并,毕竟如果将 \(i\) 减一,此时 \(\log_2(\text{lowbit}(i))\) 位之后全为 \(1\),依次合并上去就是约 \(\text{lowbit}(i)\) 个点。合并的主要复杂度在于 recycle(),此时复杂度可以表示为 \(f(2^i)=\sum_{j=0}^{i-1}2^j\cdot \log(2^j)=\sum_{j=0}^{i-1}j\cdot 2^j\).

考虑枚举每个 \(2\) 的幂作为 \(\rm lowbit\),总合并复杂度就是:

\[\begin{align} \sum_{i=1}^{\log n} f(2^i)\cdot \frac{n}{2^{i+1}}&=\sum_{i=1}^{\log n} \frac{n}{2^{i+1}}\cdot \sum_{j=1}^{i-1}j\cdot 2^j\\ &=\sum_{i=1}^{\log n} \frac{n}{2}\cdot \sum_{j=1}^{i-1}j\cdot \frac{1}{2^{i-j}} \end{align} \]

欸?后面那一坨不就是高中数列的经典题吗?化一化可以得到:

\[\begin{align} &=\sum_{i=1}^{\log n} \frac{n}{2}\cdot \left((i-1)-\sum_{j=2}^{i-1}\frac{1}{2^{i-j}}\right)\\ &<\sum_{i=1}^{\log n} {n}\cdot \log n=n\log^2 n \end{align} \]

\(\frak Code\)

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f |= (s=='-');
	while(s>='0' and s<='9')
		x = (x<<1)+(x<<3)+(s^48),
		s = getchar();
	return f?-x:x;
}

template <class T>
inline void write(T x) {
    static int writ[40],w_tp=0;
    if(x<0) putchar('-'),x=-x;
    do writ[++w_tp]=(x-x/10*10),x/=10; while(x);
    while(putchar(writ[w_tp--]^48),w_tp);
}

#include <vector>
#include <algorithm>
using namespace std;
typedef pair <int,int> pii;

const int B = 6e4+2;
const int lim = 2e5;
const int maxn = 6e4+5;
const int SIZE = maxn*40;

int n,q,a[maxn],rec[SIZE],tp,idx;
int rt[20][(lim>>1)+5],tail;
struct node {
    bool ban; int siz,son[2];
} t[SIZE];
vector <pii> v[20];

void recycle(int o) {
    if(t[o].ban || !o) return;
    rec[++ tp]=o, t[o].ban=1;
    // 因为主席树节点可能有很多爸爸,所以不妨直接将节点状态设置成 "ban" 而不是取消它与爸爸的关系
    recycle(t[o].son[0]);
    recycle(t[o].son[1]);
}

int NewNode(int p) {
    int x = tp?rec[tp--]:++idx;
    t[x] = t[p]; t[x].ban = false;
    return x;
}

void modify(int &o,int p,int l,int r,int pos) {
    o=NewNode(p); ++ t[o].siz;
    if(l==r) return;
    int mid = l+r>>1;
    if(pos<=mid) modify(t[o].son[0],t[p].son[0],l,mid,pos);
    else modify(t[o].son[1],t[p].son[1],mid+1,r,pos);
}

void rebuild(int x) {
    sort(v[x].begin(),v[x].end());
    for(int i=0; i<v[x].size(); ++i)
        modify(rt[x][i],i?rt[x][i-1]:0,1,lim,v[x][i].second);
}

void ins(int x,int y) {
    vector <pii> t; 
    t.push_back(make_pair(x,y));
    while(tail && v[tail].size()==t.size()) {
        for(int i=0; i<v[tail].size(); ++i)
            t.push_back(v[tail][i]),
            recycle(rt[tail][i]);
        -- tail;
    }
    v[++ tail] = t;
    rebuild(tail);
}

int query(int a,int b,int l,int r,int L,int R) {
    if(!b) return 0;
    if(l>=L && r<=R) return t[b].siz-t[a].siz;
    int mid = l+r>>1, ans=0;
    if(L<=mid) ans = query(t[a].son[0],t[b].son[0],l,mid,L,R);
    if(R>mid) ans += query(t[a].son[1],t[b].son[1],mid+1,r,L,R);
    return ans;
}

int ask(int x,int y,int l) {
    int ans=0;
    for(int i=1;i<=tail;++i) {
        int L=lower_bound(v[i].begin(),v[i].end(),make_pair(x-l,0))-v[i].begin()-1;
        int R=lower_bound(v[i].begin(),v[i].end(),make_pair(x+l+1,0))-v[i].begin()-1;
        ans += query(~L?rt[i][L]:0,~R?rt[i][R]:0,1,lim,y-l,y+l);
    }
    return ans;
}

int main() {
    n=read(9), q=read(9);
    for(int i=1;i<=n;++i) 
        a[i]=read(9), ins(a[i]+i,a[i]-i+B);
    char opt[10]; int x,y;
    while(q --) {
        scanf("%s %d %d",opt+1,&x,&y);
        if(opt[1]=='M') {
            a[x] = y;
            ins(a[x]+x,a[x]-x+B);
        } else
            print(ask(a[x]+x,a[x]-x+B,y),'\n');
    }
    return 0;
}

原文地址:https://www.cnblogs.com/AWhiteWall/p/12356059.html