bzoj4170 极光

题目链接:bzoj4170
题目大意:
天空中出现了许多的北极光,这些北极光组成了一个长度为n的正整数数列a[i],远古之魔书上记载到:2个位置的g
raze值为两者位置差与数值差的和:graze(x,y)=|x-y|+|a[x]-a[y]|。
要想破解天罚,就必须支持2种操作(k都是正整数):
Modify x k:将第x个数的值修改为k。
Query x k:询问有几个i满足graze(x,i)<=k。
由于从前的天罚被圣王lmc破解了,所以rhl改进了她的法术,询问不仅要考虑当前数列,还要考虑任意历史版本,
即统计任意位置上出现过的任意数值与当前的a[x]的graze值<=k的对数。(某位置多次修改为同样的数值,按多次
统计)

题解:
cdq分治+树状数组
把(x,a[x])看成一个点。那么题目要求的就是与某点曼哈顿距离≤k的点的个数。
可以知道与某点距离相等的点可是个菱形,旋转坐标系(x,y)->(x-y,x+y)。于是菱形就转成了正方形。就等于求矩阵内点的个数,差分一下就好了。
时间,x轴,y轴——经典的三维偏序用cdq来做

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 500010

struct node
{
    int x,y,id,mk;
}q[maxn],to[maxn];int cnt,n,lim;
int ans[maxn],a[maxn],c[maxn],lnum,rnum;
int mymax(int x,int y){return (x>y)?x:y;}
int lowbit(int x){return (x&(-x));}
void change(int x,int k) {for (;x<=lim && x!=0;x+=lowbit(x)) c[x]+=k;}
int query(int x){int ret=0;for (;x>0;x-=lowbit(x)) ret+=c[x];return ret;}
void solve(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)>>1;
    solve(l,mid);solve(mid+1,r);
    int i,j;
    for (i=mid+1,j=l;i<=r;i++)
    {
        for (;q[j].x<=q[i].x && j<=mid;j++)
         if (q[j].mk==0) change(q[j].y,1);
        if (q[i].mk) ans[q[i].id]+=q[i].mk*query(q[i].y);
    }
    for (i=l;i<=mid && q[i].x<=q[r].x;i++) if (q[i].mk==0) change(q[i].y,-1);
    int lnum=l,rnum=mid+1;
    for (i=l;i<=r;i++)
    {
        if ((q[lnum].x<=q[rnum].x || rnum>r) && lnum<=mid)
        {
            to[i]=q[lnum++];
        }else to[i]=q[rnum++];
    }
    for (i=l;i<=r;i++) q[i]=to[i];
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    char s[10];int m,i,x,k,tot=0;
    scanf("%d%d",&n,&m);cnt=0;
    for (i=1;i<=n;i++)
    {
        scanf("%d",&x);
        q[++cnt].x=i-x;q[cnt].y=i+x;
        lim=mymax(lim,q[cnt].x);
        lim=mymax(lim,q[cnt].y);
        q[cnt].mk=0;a[i]=x;
    }
    for (i=1;i<=m;i++)
    {
        scanf("%s%d%d",s,&x,&k);
        if (s[0]=='M')
        {
            q[++cnt].x=x-k;q[cnt].y=x+k;q[cnt].mk=0;
            a[x]=k;
        }else if (s[0]=='Q')
        {
            q[++cnt].x=x-a[x]+k;q[cnt].y=a[x]+k+x;q[cnt].mk=1;q[cnt].id=++tot;
            lim=mymax(lim,q[cnt].x);lim=mymax(lim,q[cnt].y);
            q[++cnt].x=x-a[x]-k-1;q[cnt].y=a[x]+x+k;q[cnt].mk=-1;q[cnt].id=tot;
            if (a[x]+x-k-1>0) {q[++cnt].x=x-a[x]+k;q[cnt].y=a[x]+x-k-1;q[cnt].mk=-1;q[cnt].id=tot;}
            if (a[x]+x-k-1>0) {q[++cnt].x=x-a[x]-k-1;q[cnt].y=a[x]-k+x-1;q[cnt].mk=1;q[cnt].id=tot;}
        }
    }
    memset(ans,0,sizeof(ans));
    lim++;solve(1,cnt);
    for (i=1;i<=tot;i++) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值