noi.ac 767

orz zhf

题目链接

解法

首先把问题方向转化一下,变成考虑每个点的贡献,这样就是考虑每个点左边m个比它大的数的位置,右边m个比它大的数的位置,有了这些数据就可以稍微推一下式子算出这个点一共在多少个区间中产生了贡献。
然后首先有一个 O ( n m l o g n ) O(nmlogn) 的做法,就是对于每个数,向左向右二分查找第一个比它大的数,用st表+二分,但是不能的满分,考虑更快的做法。
我们将所有数从小到达排好序,用链表维护当前位置左边的第一个比它大的位置和右边的第一个比它大的位置。然后就可以 O ( n m ) 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;
}

注意常数问题

发布了95 篇原创文章 · 获赞 9 · 访问量 3181

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/104522963