bzoj 2989: 数列

题意

给定一个长度为n的正整数数列a[i]。
定义2个位置的graze值为两者位置差与数值差的和,即graze(x,y)=|x-y|+|a[x]-a[y]|。
2种操作(k都是正整数):
1.Modify x k:将第x个数的值修改为k。
2.Query x k:询问有几个i满足graze(x,i)<=k。因为可持久化数据结构的流行,询问不仅要考虑当前数列,还要
考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的a[x]的graze值<=k的对数。(某位置多次修改为
同样的数值,按多次统计)

前言

这题之前学习CDQ分治的时候做过一次
大概就是先把曼哈顿距离转化为切比雪夫距离
然后就可以变成询问一个矩形的点了
这个的话如果不带修改的话,可以直接主席树上就可以了
但是这题带修改
一个正常的想法是用分治来支持这个东西
于是人们就思考有没有不分治的做法
答案是有的
做法就是二进制分组
论文写得很详细
挑主要部分看就好了
在40来页的样子就差不多到了
这样的话时空复杂度都是 nlog2n n l o g 2 n 的,看起来和分治差不多
但其实常数较大
然后合并的时候用归并即可
然后暴力重构的时候记得写垃圾回收,要不会爆炸

抄了一发模板

CODE:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define mk(a,b) make_pair(a,b)
const int MAX=(1<<28);
typedef pair<int,int> pr;
const int N=60005;
const int A=N+100000;
const int NN=10000005;
int rb[NN],ok[NN],tot;//垃圾回收  这个点是否已经被回收了 
int rt[20][100005];
int a[N];
int top;
vector<pr> v[20];
struct qq{int s1,s2,c;}tr[10000005];
int find (int rt1,int rt2,int l,int r,int L,int R)
{
    if (l==L&&r==R) return tr[rt1].c-tr[rt2].c;
    int mid=(l+r)>>1;
    if (R<=mid) return find(tr[rt1].s1,tr[rt2].s1,l,mid,L,R);
    else if (L>mid) return find(tr[rt1].s2,tr[rt2].s2,mid+1,r,L,R);
    else return find(tr[rt1].s1,tr[rt2].s1,l,mid,L,mid)+find(tr[rt1].s2,tr[rt2].s2,mid+1,r,mid+1,R);
}
int query (int x,int y,int c)
{
    int ans=0;
    for (int u=1;u<=top;u++)
    {
        int k=lower_bound(v[u].begin(),v[u].end(),mk(x+c,MAX))-v[u].begin();
        int k2=lower_bound(v[u].begin(),v[u].end(),mk(x-c,0))-v[u].begin();
        ans=ans+find(rt[u][k],rt[u][k2],1,A,max(1,y-c),min(y+c,A));
    }
    return ans;
}
void ins2 (int &rt1,int rt2,int l,int r,int x)
{
    rt1=rb[tot--];tr[rt1].c=tr[rt2].c+1;ok[rt1]=1;
    if (l==r) return ;
    int mid=(l+r)>>1;
    if (x<=mid) tr[rt1].s2=tr[rt2].s2,ins2(tr[rt1].s1,tr[rt2].s1,l,mid,x);
    else tr[rt1].s1=tr[rt2].s1,ins2(tr[rt1].s2,tr[rt2].s2,mid+1,r,x);
}
void Del (int &x)
{
    if (ok[x]==0) return ;
    rb[++tot]=x;tr[x].c=0;
    ok[x]=0;
    Del(tr[x].s1);Del(tr[x].s2);
    x=0;
}
void ins (int x,int y)
{
    v[++top].push_back(mk(x,y));
    ins2(rt[top][1],rt[top][0],1,A,y);
    while (top>1&&v[top-1].size()==v[top].size())
    {
        int t1=0,t2=0;vector<pr> t;
        for (int u=1;u<=v[top-1].size();u++) Del(rt[top-1][u]);
        for (int u=1;u<=v[top].size();u++) Del(rt[top][u]);
        while (t1<v[top-1].size()||t2<v[top].size())
        {
            if (t1<v[top-1].size()&&(t2==v[top].size()||v[top-1][t1]<v[top][t2]))
            {
                t.push_back(v[top-1][t1++]);
                ins2(rt[top-1][t1+t2],rt[top-1][t1+t2-1],1,A,v[top-1][t1-1].second);
            }
            else
            {
                t.push_back(v[top][t2++]);
                ins2(rt[top-1][t1+t2],rt[top-1][t1+t2-1],1,A,v[top][t2-1].second);
            }
        }
        v[top-1]=t;
        v[top].clear();
        top--;
    }
}
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for (int u=1;u<NN;u++) rb[++tot]=u,ok[u]=0;
    for (int u=1;u<=n;u++)
    {
        scanf("%d",&a[u]);
        ins(a[u]+u,a[u]-u+N);
    }

    while (q--)
    {
        char ss[10];
        int x,y;
        scanf("%s%d%d",ss,&x,&y);
        if (ss[0]=='M') a[x]=y,ins(a[x]+x,a[x]-x+N);
        else printf("%d\n",query(a[x]+x,a[x]-x+N,y));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值