noi.ac 767

orz zhf

題目鏈接

解法

首先把問題方向轉化一下,變成考慮每個點的貢獻,這樣就是考慮每個點左邊m個比它大的數的位置,右邊m個比它大的數的位置,有了這些數據就可以稍微推一下式子算出這個點一共在多少個區間中產生了貢獻。
然後首先有一個O(nmlogn)O(nmlogn)的做法,就是對於每個數,向左向右二分查找第一個比它大的數,用st表+二分,但是不能的滿分,考慮更快的做法。
我們將所有數從小到達排好序,用鏈表維護當前位置左邊的第一個比它大的位置和右邊的第一個比它大的位置。然後就可以O(nm)O(nm)

#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;
}

注意常數問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章