AGC037F
定义一个串 S S S为级别 ( k , m ) (k,m) (k,m)为:
- ∣ S ∣ = 1 |S|=1 ∣S∣=1并且 S S S中唯一的数为 k k k。
- S S S由大于等于 m m m个级别为 k − 1 k-1 k−1的串拼接而成。
每个串可以同时属于多个级别。
给出数组 a i a_i ai,求连续子序列的数量,满足存在 k k k使得这个子序列为级别 ( k , m ) (k,m) (k,m)。
m ≤ n ≤ 2 ∗ 1 0 5 m\le n\le 2*10^5 m≤n≤2∗105
(这里把题面中的 L L L换成了 m m m,为了不和下面的题解起冲突)
补题解。
按照值从小往大考虑:找到当前的最小值 m i n min min以及对应的若干个连续的区间,对于某个区间(设其长度为 l e n len len),如果 l e n = 1 len=1 len=1则是合法的区间,并将其删除;如果 l e n < m len<m len<m则是非法的区间,并将其删除;如果 l e n ≥ m len\ge m len≥m,将其替换为 ⌊ l e n m ⌋ \lfloor\frac{len}{m}\rfloor ⌊mlen⌋个 m i n + 1 min+1 min+1,然后继续操作下去。
接下来是如何计算贡献:
假如有某个串长成: 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , a , b , c 1,1,1,1,1,1,1,1,1,a,b,c 1,1,1,1,1,1,1,1,1,a,b,c, m = 3 m=3 m=3,进行了一次操作之后变成 2 , 2 , 2 , a , b , c 2,2,2,a,b,c 2,2,2,a,b,c。那么这个时候的 2 , a , b 2,a,b 2,a,b对应原序列的 1 ∗ x , a , b 1*x,a,b 1∗x,a,b( x ∈ [ 6 , 8 ] x\in[6,8] x∈[6,8])。
于是对于每个位置,分别记两个值 L , R L,R L,R。表示:某个位置,作为左(右)端点,它可以对应到原序列中的一段连续区间的长度。
维护这个东西,就可以替换之前计算答案了。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
#define ll long long
int n,m;
int a[N];
struct Node{
Node *pre,*suc;
int v,L,R;
ll s;
bool f;
} *fir,*lst;
Node *ins(int v,int L,int R,int s,Node *pre,Node *suc){
Node *nw=new Node;
*nw=(Node){
pre,suc,v,L,R,s,0};
if (pre) pre->suc=nw;
if (suc) suc->pre=nw;
return nw;
}
Node *h[N];
int nh;
bool cmph(Node *son,Node *fa){
return son->v>fa->v;}
int L[N],R[N],L_[N],R_[N];
ll calc(int L[],int R[],int k){
ll sL=0,s=0;
for (int i=1;i<=k;++i){
if (i-m+1>0)
sL+=L[i-m+1];
s+=(sL+L[i])*R[i];
}
return s;
}
ll ans;
int main(){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
ans=n;
fir=lst=ins(a[1],1,1,1,NULL,NULL);
h[nh++]=fir,fir->f=1;
for (int i=2;i<=n;++i){
lst=ins(a[i],1,1,1,lst,NULL);
if (lst->v!=lst->pre->v)
h[nh++]=lst,lst->f=1;
}
make_heap(h,h+nh,cmph);
while (nh){
Node *fir=h[0];
pop_heap(h,h+nh--,cmph);
if (fir->f==0 || fir->pre && fir->pre->v==fir->v)
continue;
int v=fir->v,k=0;
Node *p=fir,*lst=p;
for (int i=1;p && p->v==v;++i,p=p->suc)
L[i]=p->L,R[i]=p->R,++k,lst=p;
Node *pre=fir->pre,*suc=lst->suc;
if (pre) pre->suc=NULL;
if (suc) suc->pre=NULL;
fir->f=0;
fir->pre=lst->suc=NULL;
if (k==1 || k>=m){
for (Node *p=fir;p && p->v==v;p=p->suc)
ans-=p->s;
ans+=calc(L,R,k);
if (k==1)
continue;
int d=k/m;
memset(L_,0,sizeof(int)*(d+1));
memset(R_,0,sizeof(int)*(d+1));
for (int i=m;i<=k;++i)
R_[i/m]+=R[i];
for (int i=k-m+1;i>=1;--i)
L_[d-(k-i+1)/m+1]+=L[i];
for (int i=d;i>=1;--i)
suc=ins(v+1,L_[i],R_[i],0,pre,suc);
suc->f=1;
suc->s=calc(L_,R_,d);
h[nh++]=suc;
push_heap(h,h+nh,cmph);
continue;
}
}
printf("%lld\n",ans);
return 0;
}