[51nod1768]Rikka with Sequences

来源:互联网 发布:复旦大学香港大学知乎 编辑:程序博客网 时间:2024/06/14 09:09

题目描述

众所周知,萌萌哒六花不擅长数学,所以勇太给了她一些数学问题做练习,其中有一道是这样的:
勇太有一个长度为 n 的数组A 和一个大小为n×n 的二维数组B,最开始Bi,j=jk=iAk
接下来勇太进行了两种操作:
1. 给出两个整数 l 和 x ,把 A_l 的值变成x 。
2. 给出两个整数 l 和 r ,保证 l<r ,表示询问 Bl,r 的值。
在每次操作之后,勇太都会进行一次更新:Bi,j=min(Bi,j,jk=iAk)
为了锻炼六花的计算能力,勇太希望六花能够尽可能快地回答他的问题。
当然,这个问题对于萌萌哒六花来说实在是太难了,你可以帮帮她吗?
输入数据保证 n,m≤105 , A_i,x≤109

分析

历史最小区间和无法用普通线段树实现,我们换个思路。
考虑维护B数组,把i看做x坐标,j看做y坐标,它就是二维空间的点集嘛,使用二维线段树维护?然而历史标记不能拆分,打起来也很麻烦。
考虑使用KD树,怎么用呢?使用KD树常用小技巧,离线先,把所有询问点抽出来建立KD树,然后考虑每个操作的影响。改变一个a_x,就相当于区间加减一堆点的值,这个区间的左下角是(1,x),右上角是(x,n);查询相当于查询一个点的历史最小值。
用吉如一论文经典维护历史标记做法即可。
KD树区间打标记的时候,KD树控制的区间和标记区间相离则退出,完全包含则打区间标记;否则递归下传,注意判断当前KD树实际代表点是否被覆盖,若覆盖则直接把改变加上去。
那么复杂度是O(nn)的,具体证明见KD树课件。

代码

#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>#include<set>#include<map>using namespace std;#define cmax(a,b) (a=(a>b)?a:b)#define cmin(a,b) (a=(a<b)?a:b)#define fo(i,j,k) for(i=j;i<=k;i++)#define fd(i,j,k) for(i=j;i>=k;i--)typedef long long ll;typedef double db;const int N=4e5+5,mo=998244353,Q=1e6+5; struct rec{    int s,x,y,id,pid;}que[N];struct kd_tree{    int lx,ly,rx,ry;    int x,y,ls,rs;    ll v,mnv,tag,mntag;}tr[N];struct vec{    int x,y;}d[N];int ws,m,rt,lx,ly,rx,ry,a[N],n,i,td,x,v,ax,ay,tp;ll prt[N];multiset<vec> trs;bool cmp(vec a,vec b){    if (!ws) return a.x<b.x;    return a.y<b.y;}bool operator <(vec a,vec b){    return a.x<b.x||a.x==b.x&&a.y<b.y;}int w[20],I;void Print(ll x){    w[0]=0;    while (x) w[++w[0]]=x%10,x/=10;    if (!w[0]) putchar('0');    fd(I,w[0],1) putchar(w[I]+'0');    putchar('\n');}void mt(int x,int y){    cmax(tr[x].rx,tr[y].rx);    cmax(tr[x].ry,tr[y].ry);    cmin(tr[x].lx,tr[y].lx);    cmin(tr[x].ly,tr[y].ly);}int make(int l,int r,int s){    ws=s;    int m=(l+r)/2;    nth_element(d+l,d+m,d+r+1,cmp);    s^=1;    tr[m].lx=tr[m].rx=tr[m].x=d[m].x;    tr[m].ly=tr[m].ry=tr[m].y=d[m].y;    if (l<m) mt(m,tr[m].ls=make(l,m-1,s));    if (m<r) mt(m,tr[m].rs=make(m+1,r,s));    return m;}void down(int x){    int ls=tr[x].ls,rs=tr[x].rs;    cmin(tr[ls].mntag,tr[x].mntag+tr[ls].tag);    cmin(tr[rs].mntag,tr[x].mntag+tr[rs].tag);    cmin(tr[x].mnv,tr[x].v+tr[x].mntag);    tr[ls].tag+=tr[x].tag;    tr[rs].tag+=tr[x].tag;    tr[x].v+=tr[x].tag;    tr[x].tag=tr[x].mntag=0;}void change(int x){    kd_tree &y=tr[x];    if (!x||y.rx<lx||y.lx>rx||y.ry<ly||y.ly>ry) return ;    if (lx<=y.lx&&y.rx<=rx&&ly<=y.ly&&y.ry<=ry)// all in    {        y.tag+=v;        cmin(y.mntag,y.tag);        return ;     }    down(x);    if (lx<=y.x&&y.x<=rx&&ly<=y.y&&y.y<=ry) // this in    {        y.v+=v;        cmin(y.mnv,y.v);    }    change(y.ls);    change(y.rs);}void thr(int x){    if (!x) return ;    down(x);    tr[x].mnv=tr[x].v;    thr(tr[x].ls);    thr(tr[x].rs);}ll get(int x){    if (!x||ax<tr[x].lx||ax>tr[x].rx||ay<tr[x].ly||ay>tr[x].ry) return 1e15;    down(x);    if (tr[x].x==ax&&tr[x].y==ay) return tr[x].mnv;    return min(get(tr[x].ls),get(tr[x].rs));}void predo(){    rt=make(1,td,0);    fo(i,1,n)    {        lx=1;rx=i;ly=i;ry=n;v=a[i];        change(rt);    }    thr(rt);}int main(){    freopen("t1.in","r",stdin);    freopen("t1.out","w",stdout);    scanf("%d %d",&n,&m);    fo(i,1,n) scanf("%d",a+i);    fo(i,1,m)    {        scanf("%d %d %d",&que[i].s,&que[i].x,&que[i].y);        if (que[i].s==2)        {            d[++td].x=que[i].x;            d[td].y=que[i].y;            if (trs.find(d[td])!=trs.end())                 td--;            else trs.insert(d[td]);        }    }    predo();// range from 1~n,1~n    fo(i,1,m)    {        if (que[i].s==1)        {            x=que[i].x;            lx=1;rx=x;ly=x;ry=n;            v=que[i].y-a[x];            change(rt);            a[x]=que[i].y;        }else        {            ax=que[i].x;ay=que[i].y;            prt[++tp]=get(rt);        }    }    fo(i,1,tp) Print(prt[i]);}
原创粉丝点击