【題解】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;
}
相關文章
相關標籤/搜索