orz zhf
解法
首先把問題方向轉化一下,變成考慮每個點的貢獻,這樣就是考慮每個點左邊m個比它大的數的位置,右邊m個比它大的數的位置,有了這些數據就可以稍微推一下式子算出這個點一共在多少個區間中產生了貢獻。
然後首先有一個的做法,就是對於每個數,向左向右二分查找第一個比它大的數,用st表+二分,但是不能的滿分,考慮更快的做法。
我們將所有數從小到達排好序,用鏈表維護當前位置左邊的第一個比它大的位置和右邊的第一個比它大的位置。然後就可以
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int mod=998244353;
inline int read(){
char c=getchar();int t=0,f=1;
while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,m,a[maxn],x[55],y[55];
struct node{
int a,p;
}b[maxn];
bool cmp(node a,node b){
if(a.a!=b.a)
return a.a<b.a;
return a.p<b.p;
}
int l[maxn],r[maxn];
inline int am(int x){
return x>=mod?x-mod:x;
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++){a[i]=read();b[i].a=a[i];b[i].p=i;l[i]=i-1,r[i]=i+1;}
sort(b+1,b+1+n,cmp);
int ans=0;
for(int i=1;i<=n;i++){
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
int pos=b[i].p;
x[0]=pos;int gy=m;
for(int j=1;j<=m;j++){
x[j]=l[pos];
if(x[j]==0){
x[j+1]=-1;break;
}
pos=l[pos];
}
pos=b[i].p;
y[0]=pos;
for(int j=1;j<=m;j++){
y[j]=r[pos];
if(y[j]==n+1){
gy=j;break;
}
pos=r[pos];
}
pos=b[i].p;
for(int j=1;j<=m;j++){
if(x[j]==-1)break;
int tmp=x[j-1]-x[j];
ans=am(ans+1ll*a[pos]*tmp%mod*(y[min(gy,m-j+1)]-y[0])%mod);
}
r[l[pos]]=r[pos];
l[r[pos]]=l[pos];
}
printf("%d\n",ans);
return 0;
}
注意常數問題