• 【题解】Luogu P5294 [HNOI2019]序列


    原题传送门

    题意:给你一个长度为(n)的序列(A),每次询问修改一个元素(只对当前询问有效),然后让你找到一个不下降序列(B),使得这两个序列相应位置之差的平方和最小,并输出这个最小平方和

    观察样例说明,发现一个很有趣的性质,(B)中数字相同的一段的数字正好是(A)中这段数字的平均数

    那我们就珂以猜想:最优解的形式一定为分成若干段,每一段的(B_i)即取其中(A_i)的平均数,同时保证(B)的有序性(这篇论文好像有证明)

    如何求出最优的(B)?我们珂以使用单调栈

    考虑枚举(i),将([i,i])塞进单调栈中,然后比较栈顶区间的平均值和栈顶下面一个区间的平均值。如果栈顶区间平均值较小,则不满足不下降性质,因此我们将这两个区间合并,然后继续与再下面一个区间进行比较……

    如何合并答案?我们只需要维护维护区间元素个数,区间元素和,区间元素平方和即可(像方差那题一样)

    接下来考虑如何修改。我们在求整个序列的最优解时,同主席树维护(A)每个前缀的单调栈(pre)。类似的,从后向前也跑一次,用主席树维护(A)每个后缀的单调栈(suf)

    假设我们将位置(pos)的值修改成了(val)。设修改后最优解中(pos)所在的区间为([L,R]),如果我们能够快速求出(L,R)我们就珂以快速得出答案。

    注意到单调栈中的一段数的任何前缀平均值都大于去掉该前缀剩余的数的平均值,所以(L)(pre[pos-1])中某个区间的(L)(pos)(R)(suf[pos+1])中某个区间的(R)(pos)

    二分(R)的位置,在主席树(pre[pos-1])上二分求出符合的(L),判断是否合法。最终找到最小的合法的(R),算出(L),就珂以求出答案了

    #include <bits/stdc++.h>
    #define N 100005
    #define ll long long
    #define mod 998244353 
    #define getchar nc
    using namespace std;
    inline char nc(){
        static char buf[100000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int read()
    {
        register int x=0,f=1;register char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return x*f;
    }
    inline void write(register int x)
    {
        if(!x)putchar('0');if(x<0)x=-x,putchar('-');
        static int sta[20];register int tot=0;
        while(x)sta[tot++]=x%10,x/=10;
        while(tot)putchar(sta[--tot]+48);
    }
    inline int Max(register int a,register int b)
    {
        return a>b?a:b;
    }
    int n,m,pre[N],prelen[N],suf[N],suflen[N],tot;
    pair<int,int> s[N];
    ll inv[N];
    struct node{
        ll x,y,z;
        node(){x=y=z=0;}
        node(register ll _x,register ll _y,register ll _z){x=_x,y=_y,z=_z;}
        inline ll cal()
        {
            return (z+mod-(y%mod)*(y%mod)%mod*inv[x]%mod)%mod;
        }
    }sum[N];
    inline node Init(register ll v)
    {
        return node(1,v,v*v%mod);
    }
    inline node operator + (node a,node b){return (node){a.x+b.x,a.y+b.y,(a.z+b.z)%mod};}
    inline node operator - (node a,node b){return (node){a.x-b.x,a.y-b.y,(a.z-b.z+mod)%mod};}
    inline bool operator < (node a,node b){
        if(!a.x)
            return b.x;
        if(!b.x)
            return 0;
        return (double)a.y/a.x<(double)b.y/b.x;
    }
    inline node cal(register int l,register int r)
    {
        if(!l&&!r)
            return node(0,0,0);
        return sum[r]-sum[l-1];
    }
    struct seg{
        int ls,rs,L,R,ans,Rp;
        inline void merge(register seg a,register seg b)
        {
            L=a.L;
            R=Max(a.R,b.R);
            ans=(a.ans+b.ans)%mod;
            Rp=a.Rp;
        }
    }tr[N*80];
    inline void modify(register int &x,register int l,register int r,register int pos,register int L,register int R)
    {
        tr[++tot]=tr[x];
        x=tot;
        if(l==r)
            tr[x]=(seg){0,0,L,R,cal(L,R).cal(),R};
        else
        {
            int mid=l+r>>1;
            if(pos<=mid)
                modify(tr[x].ls,l,mid,pos,L,R);
            else
                modify(tr[x].rs,mid+1,r,pos,L,R);
            tr[x].merge(tr[tr[x].ls],tr[tr[x].rs]);
        }
    }
    inline int query_suf(register int x,register int l,register int r,register int pos)
    {
        if(l==r)
            return tr[x].R;
        int mid=l+r>>1;
        if(pos<=mid)
            return query_suf(tr[x].ls,l,mid,pos);
        else
            return query_suf(tr[x].rs,mid+1,r,pos);
    }
    inline int query_pre(register int x,register int l,register int r,register int pos,register node &val)
    {
        if(r<=pos)
        {
            node twh=cal(tr[x].L,tr[x].R),tlf=cal(tr[x].L,tr[x].Rp);
            if(!(tlf<val+twh))
            {
                val=val+twh;
                return 0;
            }
            if(l==r)
                return tr[x].R;
        }
        int mid=l+r>>1,res=0;
        if(pos>mid)
            res=query_pre(tr[x].rs,mid+1,r,pos,val);
        if(res)
            return res;
        return query_pre(tr[x].ls,l,mid,pos,val);
    }
    int main()
    {
        n=read(),m=read();
        inv[1]=1;
        for(register int i=2;i<=n;++i)
            inv[i]=inv[mod%i]*(mod-mod/i)%mod;
        for(register int i=1;i<=n;++i)
            sum[i]=sum[i-1]+Init(read());
        for(register int i=1,top=0;i<=n;++i)
        {
            pre[i]=pre[i-1];
            int l=i;
            while(top&&!(cal(s[top].first,s[top].second)<cal(l,i)))
                modify(pre[i],1,n,top,0,0),l=s[top--].first;
            s[prelen[i]=++top]=make_pair(l,i);
            modify(pre[i],1,n,top,l,i);
        }
        for(register int i=n,top=0;i;--i)
        {
            suf[i]=suf[i+1];
            int r=i;
            while(top&&!(cal(i,r)<cal(s[top].first,s[top].second)))
                modify(suf[i],1,n,top,0,0),r=s[top--].second;
            s[suflen[i]=++top]=make_pair(i,r);
            modify(suf[i],1,n,top,i,r);
        }
        write(tr[pre[n]].ans),puts("");
        while(m--)
        {
            int x=read(),y=read();
            int l=0,r=suflen[x+1]-1,res=r+1;
            while(l<=r)
            {
                int mid=l+r>>1;
                int rp=mid?query_suf(suf[x+1],1,n,suflen[x+1]-mid+1):x;
                node val=Init(y)+cal(x+1,rp);
                int lp=x>1?query_pre(pre[x-1],1,n,prelen[x-1],val):1;
                if(val<cal(rp+1,query_suf(suf[x+1],1,n,suflen[x+1]-mid)))
                    res=mid,r=mid-1;
                else
                    l=mid+1;
            }
            int rp=res?query_suf(suf[x+1],1,n,suflen[x+1]-res+1):x;
            node val=Init(y)+cal(x+1,rp);
            int lp=x>1?query_pre(pre[x-1],1,n,prelen[x-1],val):1;
            write((val.cal()+tr[pre[lp]].ans+tr[suf[rp+1]].ans)%mod),puts("");
        }
        return 0;
    }
    
  • 相关阅读:
    cocos2dx-lua捕获用户touch事件的几种方式
    Java并发编程之闭锁CountDownLatch简单介绍
    opencv视频播放
    完全备份、差异备份以及增量备份的区别
    如何实现文件增量同步——算法
    Oracle提示“资源正忙,需指定nowait”的解决方案
    oracle之报错:ORA-00054: 资源正忙,要求指定 NOWAIT
    一次oracle大量数据删除经历
    rownum的使用-分页
    sql语句分页多种方式ROW_NUMBER()OVER
  • 原文地址:https://www.cnblogs.com/yzhang-rp-inf/p/10960923.html
Copyright © 2020-2023  润新知