A - CF553E Kyoya and Train
B - AGC037F Counting of Subarrays
Sol
考虑如何检查一个序列是否能够属于某个级别 ( k , l ) (k,l) (k,l):
- 如果序列中只有一种元素,只需检查序列的长度是否为 1 1 1或者大于等于 L L L就可以了。
- 如果序列中的元素多于一种,则考虑序列中最小的元素 m m m,找出所有的极长的连续为 m m m的段 [ l 1 , r 1 ] , [ l 2 , r 2 ] ⋯ [ l k , r k ] [l_1,r_1],[l_2,r_2]\cdots [l_k,r_k] [l1,r1],[l2,r2]⋯[lk,rk],将每一段替换成 ⌊ r i − l i + 1 L ⌋ \lfloor {r_i - l_i + 1 \over L}\rfloor ⌊Lri−li+1⌋个 m + 1 m+1 m+1。如果存在一段长度不到 L L L则序列不合法。
- 重复2.直到序列中只有一个元素。
这样检查的复杂度是 O ( n ) O(n) O(n)的,因为每一次是用 O ( L ) O(L) O(L)的时间代价让序列中的元素减少了 L − 1 L-1 L−1个。
对原序列模拟这个过程,并对每一个新的元素记录下: L i L_i Li表示原序列中有多少个位置,满足当它作为被检查的区间左端点的时候,到这一步左端点为 i i i; R i R_i Ri表示原序列中有多少个位置,满足当它作为被检查的区间右端点的时候,到这一步右端点为 i i i。每一步的时候对于最小元素构成的极长段统计 ∑ i = j 或 者 j − i + 1 ≥ L L i ⋅ R j \sum_{i=j 或者j-i+1 \ge L} L_i \cdot R_j ∑i=j或者j−i+1≥LLi⋅Rj并加入答案就可以了。注意由于在把 m m m改成 m + 1 m+1 m+1之前,极长段内的元素之间的贡献已经统计过了,所以改成 m + 1 m+1 m+1之后要减去这些由 m m m变成 m + 1 m+1 m+1的元素构成的极长段内的贡献。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=6e5+10,inf=2e9;
int fa[N],ch[N][2],mx[N],mi[N],sz[N],val[N],ncnt,rt;
int Lx[N],Rx[N],vis[N];
int L;
inline void push_up(int c) {
sz[c]=sz[ch[c][0]]+sz[ch[c][1]]+1;
mi[c]=min(min(mi[ch[c][0]],mi[ch[c][1]]),vis[c]?inf:val[c]);
mx[c]=max(max(mx[ch[c][0]],mx[ch[c][1]]),vis[c]?0:val[c]);
}
inline bool get(int x) { return ch[fa[x]][1]==x; }
inline void rotate(int x) {
int f=fa[x],ff=fa[f],d=get(x);
fa[x]=ff; if(ff) ch[ff][ch[ff][1]==f]=x;
fa[ch[x][d^1]]=f,ch[f][d]=ch[x][d^1];
fa[f]=x,ch[x][d^1]=f; push_up(f),push_up(x);
}
void splay(int x,int gl) {
for(int f=fa[x];f!=gl;rotate(x),f=fa[x])
if(fa[f]!=gl) rotate(get(x)==get(f)?f:x);
if(!gl) rt=x;
}
int kth(int x,int k) {
if(sz[ch[x][0]]+1==k) return x;
if(k<=sz[ch[x][0]]) return kth(ch[x][0],k);
return kth(ch[x][1],k-sz[ch[x][0]]-1);
}
void debug(int x) {
if(!x) return;
debug(ch[x][0]);
printf("%d ",vis[x]?-1:val[x]);
debug(ch[x][1]);
push_up(x);
}
void Debug() { printf("debug:"),debug(rt),puts(""); sz[0]=0; }
int split(int l,int r) {
int x=kth(rt,l-1),y=kth(rt,r+1);
splay(x,0),splay(y,x);
return ch[y][0];
}
int lv[N],rv[N],V[N],Vi[N],lim;
void build(int l,int r,int &c,int f) {
if(l>r) return (void)(c=0);
int mid=l+r>>1; c=++ncnt;
val[c]=V[mid],vis[c]=Vi[mid],fa[c]=f;
Lx[c]=lv[mid],Rx[c]=rv[mid];
build(l,mid-1,ch[c][0],c);
build(mid+1,r,ch[c][1],c);
push_up(c);
}
struct item {
int p,l,r;
item(int p=0,int l=0,int r=0): p(p),l(l),r(r) {}
};
vector<item> A;
void find(int x,int pr) {
if(mi[x]>lim) return;
find(ch[x][0],pr); pr+=sz[ch[x][0]];
if(val[x]==lim&&!vis[x]) A.PB(item(pr+1,Lx[x],Rx[x]));
find(ch[x][1],pr+1);
}
int main() {
mi[0]=inf;
int n; rd(n),rd(L);
for(int i=1;i<=n;++i) rd(V[i]),lv[i]=rv[i]=1;
Vi[0]=Vi[n+1]=1;
build(0,n+1,rt,0);
Vi[0]=Vi[n+1]=0;
ll ans=0;
while(true) {
A.clear(),lim=mi[rt],find(rt,0);
for(int l=0,r;l<A.size();l=r+1) {
r=l; while(r+1<A.size()&&A[r+1].p==A[r].p+1) ++r;
ll s=0;
for(int i=l;i<=r;++i) {
if(i-L+1>=l) s+=A[i-L+1].l;
ans+=s*A[i].r;
ans+=A[i].l*A[i].r;
}
}
if(mi[rt]>=mx[rt]) break;
int pre=0;
for(int l=0,r;l<A.size();l=r+1) {
r=l; while(r+1<A.size()&&A[r+1].p==A[r].p+1) ++r;
int len=(r-l+1)/L;
int u=split(A[l].p+pre,A[r].p+pre);
pre-=r-l+1; pre+=max(len,1);
if(!len) {
vis[u]=1;
ch[u][0]=ch[u][1]=0;
push_up(u),push_up(fa[u]),push_up(rt);
continue;
}
for(int i=0;i<len;++i) lv[i]=rv[i]=0,V[i]=lim+1;
for(int i=l+L-1;i<=r;++i) rv[(i-l+1)/L-1]+=A[i].r;
for(int i=l;i<=r-L+1;++i) lv[len-(r-i+1)/L]+=A[i].l;
for(int i=0,s=0;i<len;++i) {
if(i-L+1>=0) s+=lv[i-L+1];
ans-=s*rv[i];
ans-=lv[i]*rv[i];
}
build(0,len-1,ch[fa[u]][0],fa[u]);
push_up(fa[u]),push_up(rt);
}
}
printf("%lld",ans);
return 0;
}