【JZOJ 4939】平均值

Description

这里写图片描述

Solution

首先了,除才逆元的情况下显然是可以合并的,
所以,当一个区间的 mex 值没有变化时,就可以吧mex值相同的 rl+1 一起结算,
nxi 表示下一个与 ai 相同的数的位置, mxi 表示 mex(l,i) ,l为枚举的左端点,
我们先枚举一个l为左端点,
那么现在,我需要吧l往右移一位,那么会对 l ~(nxl1)这个区间的mex产生影响,
有因为从l开始的mex一定是不下降的,
那么当第一个i满足 mxi>=al ,并且 i<=nxl1 ,那么 mxi ~ mxnxl1 都要变成 al
在此之前,我们先要结算这个区间的贡献,
在i~ nxl1 这个区间中,我们先找到一个区间使得mx值相同,又因为我们可以记录第一次mx为这个值的时间,于是就可以求出每个位置分别被乘上了怎么逆元,自己在草稿纸上写一下就可以发现这个用公式很好求,
为了完成上面的操作,可以使用线段树,

复杂度: O(nlog(n))

虽然说你每次要找到mx相同的区间,这个要很多的时间,但每次找完了以后,你每个访问的区间的mx都会变为一样的,所以复杂度是可以保证的,

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define min(q,w) ((q)<(w)?(q):(w))
#define max(q,w) ((q)>(w)?(q):(w))
using namespace std;
typedef long long LL;
const int N=6e5,mo=998244353;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n,m;
int a[N],zx[N];
struct qqww{int v,n;}A[N];
int z[N];
LL ans;
LL ny[N],nys[N],nyss[N];
struct wwqq
{int la,lat,ti,l,r,mx;bool sm;}b[10*N];
bool PX(qqww q,qqww w){return q.v<w.v||(q.v==w.v&&q.n<w.n);}
LL ksm(LL q,LL w)
{
    q%=mo;
    LL ans=1;
    while(w)
    {
        if(w&1)ans=ans*q%mo;
        q=q*q%mo;w>>=1;
    }
    return ans;
}
LL H(int l,int r,int s,int t)
{
    l=l-s+1,r=r-s+1;
    LL ans=(nyss[r]-nyss[r-(t-s+1)]+mo)%mo;
    ans=(ans-nyss[l-1]+nyss[max(0,l-(t-s+1)-1)]+mo)%mo;
    return ans;
}
void merge(wwqq &e,wwqq q,wwqq w)
{
    e.sm=q.sm&&w.sm&&(q.mx==w.mx);
    e.mx=max(q.mx,w.mx);
    e.ti=q.ti;
}
void build(int l,int r,int e)
{
    b[e].l=l,b[e].r=r;
    b[e].ti=1;
    b[e].la=-1;
    if(l==r)
    {
        b[e].mx=b[e].mx=A[l].v;
        b[e].sm=1;
        return;
    }
    int t=(l+r)>>1;
    build(l,t,e*2);build(t+1,r,e*2+1);
    merge(b[e],b[e*2],b[e*2+1]);
}
void doit(int e,int la,int lat)
{
    if(b[e].la!=-1)
    {
        b[e].mx=b[e].la,b[e].ti=b[e].lat;
    }
    b[e].la=la,b[e].lat=lat;
    b[e].sm=1;
    ans=(ans+b[e].mx*H(b[e].l,b[e].r,b[e].ti,b[e].lat))%mo;
}
void putdown(int l,int r,int e)
{
    if(b[e].la==-1)return;
    b[e].mx=b[e].la,b[e].ti=b[e].lat+1;
    if(l!=r)
    {
        b[e*2].la=b[e*2+1].la=b[e].la;
        b[e*2].lat=b[e*2+1].lat=b[e].lat;
    }
    b[e].la=-1;
}
void change(int l,int r,int e,int l1,int r1,int la,int lat)
{
    int t=(l+r)>>1;
    putdown(l,t,e*2),putdown(t+1,r,e*2+1);
    if(b[e].mx<la)return;
    if(l>=l1&&r<=r1&&b[e].sm)
    {
        if(b[e].mx>=la)
        {
            doit(e,la,lat);
            putdown(l,r,e);
        }
        return;
    }
    if(r1<=t)change(l,t,e*2,l1,r1,la,lat);
        else if(t<l1)change(t+1,r,1+e*2,l1,r1,la,lat);
            else 
            {
                change(l,t,e*2,l1,t,la,lat);
                change(t+1,r,e*2+1,t+1,r1,la,lat);
            }
    merge(b[e],b[e*2],b[e*2+1]);
}
int main()
{
    freopen("average.in","r",stdin);
    freopen("average.out","w",stdout);
    LL q;
    read(n);
    fo(i,1,n)A[i].v=read(a[i]),A[i].n=i;
    fo(i,1,n)
    {
        ny[i]=ksm(i,mo-2),
        nys[i]=(ny[i]+nys[i-1])%mo;
        nyss[i]=(nys[i]+nyss[i-1])%mo;
        zx[i]=n+1;
    }
    sort(A+1,A+1+n,PX);
    fo(i,1,n-1)if(A[i].v==A[i+1].v)zx[A[i].n]=A[i+1].n;
    fo(j,0,n)z[j]=0;
    q=0;
    fo(i,1,n)
    {
        z[a[i]]++;
        while(z[q])q++;
        A[i].v=q;
    }
    build(1,n,1);
    fo(i,1,n)
    {
        change(1,n,1,i,zx[i]-1,a[i],i);
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值