原題連結
考慮對於每個 \(i\),算出向左擴充套件到 \(1\) 時向右至少和至多擴充套件到哪裡,記為 \(minr\) 和 \(maxr\)。
那麼也就是說每個 \(i\) 會對 \(minr\sim maxr\) 做出貢獻,差分一下就可以了。重點是怎麼計算這兩個東西。
先說 \(maxr\)。如果暴力跳,過程是:先向左擴充套件直到不能擴充套件,然後再向右擴充套件到不能擴充套件。不斷重複直到左邊界到 \(1\) 或者兩邊都無法繼續擴充套件宣告失敗。
然後你想起了剛剛做完的 E1,每次擴充套件一定是找到一個最近的最大值,把最大值之前的都加上,再看看能不能也把這個最大值也加上。這個可以用 ST 表和二分 \(\mathcal{O}(\log V\log n)\) 解決。
具體地說,記 \(l,r\) 為當前左右邊界,\(s\) 為字首和,在向左擴充套件時,要找到一個最大的 \(x\) 使得 \(s_r-s_{l-1}<a_{l-1}\),移項變為 \(s_r<a_{l-1}+s_{l-1}\),用 ST 表維護 \(a_i+s_i\) 的最大值。二分時每次檢查右區間是否符合條件,如果是則增大左邊界,否則減小右邊界。向右擴充套件基本同理,式子稍有變化,換成了維護最小值。
其實 \(maxr\) 也可以雙指標來著然後再看 \(minr\)。有區別的是向右擴充套件時不是無腦擴充套件,而是擴充套件到剛好夠左邊能跨過下一個最大值。那麼只需每次向右擴充套件之後再二分一下,嘗試把 \(r\) 往回減一些。這個不需要 ST 表。
來看一下時間複雜度。每次當前區間總和因為跨過了一個最大值,所以至少增加了一倍,那麼也就最多增加 \(\log V\) 次。再加上二分和列舉 \(i\) 的複雜度,總複雜度 \(\mathcal{O}(n\log n\log V)\)。
細節說多不多說少不少。
#include<bits/stdc++.h>
#define int long long
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
const int N=2e5+5;
const int inf=0x3f3f3f3f;
inline int read();
int n,m,k;int a[N];
int f[30][N],g[30][N];
int ans[N],minr[N],maxr[N],s[N];
int getmax(int x,int y){
int s=__lg(y-x+1);
return max(f[s][x],f[s][y-(1<<s)+1]);
}
int getmin(int x,int y){
int s=__lg(y-x+1);
return min(g[s][x],g[s][y-(1<<s)+1]);
}
void work(){
n=read();read();
FOR(i,1,n)a[i]=read(),s[i]=s[i-1]+a[i],ans[i]=0;
a[n+1]=0;
FOR(i,1,n)f[0][i]=a[i]+s[i],g[0][i]=s[i]-a[i+1];
for(int j=1;1<<j<=n;++j)
for(int i=1;i+(1<<j)-1<=n;++i){
f[j][i]=max(f[j-1][i],f[j-1][i+(1<<j-1)]);
g[j][i]=min(g[j-1][i],g[j-1][i+(1<<j-1)]);
}
int L,R,l,r,mid;
FOR(i,1,n){
l=i,r=i;
while(1){
bool flag=0;
L=1,R=l;
while(L<R){
mid=L+R>>1;
if(s[r]<getmax(mid,R-1))L=mid+1;
else R=mid;
}
if(l!=L)l=L,flag=1;
if(l==1||r==n)break;
L=r,R=n;
while(L<R){
mid=L+R>>1;
if(getmin(L,mid)<s[l-1])R=mid;
else L=mid+1;
}
if(r!=L){
flag=1;
int x=L;
L=r+1,R=x;
while(L<R){
mid=L+R>>1;
if(s[mid]-s[l-1]>=a[l-1])R=mid;
else L=mid+1;
}
r=R;
}
if(!flag)break;
}
if(l==1)minr[i]=r;
else minr[i]=inf;
l=i,r=i;
while(1){
bool flag=0;
L=r,R=n;
while(L<R){
mid=L+R>>1;
if(getmin(L,mid)<s[l-1])R=mid;
else L=mid+1;
}
if(r!=L)flag=1,r=R;
L=1,R=l;
while(L<R){
mid=L+R>>1;
if(s[r]<getmax(mid,R-1))L=mid+1;
else R=mid;
}
if(l!=L)l=L,flag=1;
if(!flag)break;
}
if(l==1)maxr[i]=r;
else maxr[i]=inf;
}
FOR(i,1,n)
if(maxr[i]!=inf&&minr[i]<=maxr[i])
ans[minr[i]]++,ans[maxr[i]+1]--;
FOR(i,1,n)ans[i]+=ans[i-1];
FOR(i,1,n)printf("%lld ",ans[i]);puts("");
}
signed main()
{
int T=read();
while(T--)work();
return 0;
}
inline int read()
{
int x=0,f=1;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^48);ch=getchar();}
return f*x;
}