[UOJ213][UNR #1]争夺圣杯

uoj

description

一个长为(n)的序列,给定一个参数(m),求所有长度为(m)的区间的最大值之和。
对于所有的(min[1,n])你都需要分别求出答案然后异或起来。
(nle10^6)

sol

枚举区间长度(m)看上去不好做,我们改变一下顺序,枚举每个位置(i),考虑它对每个长度的答案的贡献。
(L_i)(i)左边第一个大于等于(a_i)的数的出现位置,(R_i)(i)右边第一个大于(一定需要有一边不能取等)(a_i)的数的出现位置。
那么显然(a_i)这个数能够贡献的区间就必须满足(lin[L_i+1,i],rin[i,R_i-1])
(p=min(i-L_i,R_i-i),q=max(i-L_i,R_i-i))
对于长度(xin[1,p-1])的区间,(a_i)对它的贡献是(x imes a_i)
对于长度(xin[p,q-1])的区间,(a_i)对它的贡献是(p imes a_i)
对于长度(xin[q,p+q-1])的区间,(a_i)对它的贡献是((p+q-x) imes a_i)
三段分别处理,相等于是在答案数组上区间加一个一次函数,开两个差分数组分别维护一下就行了。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 1e6+5;
const int mod = 998244353;
int n,a[N],L[N],R[N],S[N],top,s[N],ss[N],ans;
inline void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
inline void mns(int &x,int y){x-=y;if(x<0)x+=mod;}
void cover(int l,int r,int a,int b){
	add(ss[l],a);mns(ss[r+1],a);
	add(s[l],b);mns(s[r+1],b);
}
int main(){
	n=gi();
	for (int i=1;i<=n;++i) a[i]=gi();
	for (int i=1;i<=n;++i){
		while (top&&a[S[top]]<=a[i]) --top;
		L[i]=S[top];S[++top]=i;
	}
	S[top=0]=n+1;
	for (int i=n;i;--i){
		while (top&&a[S[top]]<a[i]) --top;
		R[i]=S[top];S[++top]=i;
	}
	for (int i=1;i<=n;++i){
		int p=i-L[i],q=R[i]-i;if (p>q) swap(p,q);a[i]%=mod;
		cover(1,p-1,a[i],0);
		cover(p,q-1,0,1ll*p*a[i]%mod);
		cover(q,p+q-1,mod-a[i],1ll*(p+q)*a[i]%mod);
	}
	for (int i=1;i<=n;++i) add(s[i],s[i-1]),add(ss[i],ss[i-1]),ans^=(1ll*ss[i]*i+s[i])%mod;
	printf("%d
",ans);return 0;
}
原文地址:https://www.cnblogs.com/zhoushuyu/p/9452063.html