uoj#213 [UNR #1]争夺圣杯 单调栈+差分

版权声明:转吧转吧这条东西只是来搞笑的。。 https://blog.csdn.net/jpwang8/article/details/89341689

Description


给一个长度为n的序列,定义一个区间的权值为区间内最大值。记ans[i]表示长度为i的所有区间权值之和膜998244353
问ans的异或和
n 1 e 6 n\le1e6

Solution


考虑求出元素i作为最大值的区间[L[i],R[i]],记左右区间中较短的为mn,较长的为mx。我们讨论一下对于各个长度的区间这个位置的贡献是啥
当x<=mn时,区间至少要包含i这个位置,那么ans[x]就要加上x*a[i]
当mn<x<=mx时,不难发现系数恰好就是mn
当mx<x<=mx+mn-1时,系数就是mx+mn-x
可以发现1、3是一次函数的形式,2是常数。我们可以二阶差分维护这个东西,当然也可以分开维护一次项前系数和常数,最后乘上各自的x就可以了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)

typedef long long LL;
const int MOD=998244353;
const int N=2000005;

LL a[N],L[N],R[N],st[N],s[N],t[N],top;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void upd(LL &x,LL v) {
	x=((x+v)%MOD+MOD)%MOD;
}

int main(void) {
	freopen("data.in","r",stdin);
	int n=read();
	rep(i,1,n) a[i]=read();
	st[top=1]=L[1]=1;
	rep(i,2,n) {
		while (top&&a[i]>a[st[top]]) top--;
		st[++top]=i; L[i]=st[top-1]+1;
	}
	st[top=0]=n+1;
	st[top=1]=n,R[n]=n;
	drp(i,n-1,1) {
		while (top&&a[i]>=a[st[top]]) top--;
		st[++top]=i; R[i]=st[top-1]-1;
	}
	rep(i,1,n) {
		LL l=i-L[i]+1,r=R[i]-i+1;
		LL mn=std:: min(l,r),mx=r+l-mn;
		upd(s[1],a[i]),upd(s[mn+1],MOD-a[i]);
		upd(s[mx+1],MOD-a[i]),upd(s[mx+mn],a[i]);
		upd(t[mn+1],mn*a[i]%MOD),upd(t[mx+1],MOD-mn*a[i]%MOD);
		upd(t[mx+1],(mx+mn)%MOD*a[i]%MOD),upd(t[mx+mn],MOD-(mx+mn)%MOD*a[i]%MOD);
	}
	rep(i,1,n) upd(s[i],s[i-1]);
	rep(i,1,n) upd(t[i],t[i-1]);
	rep(i,1,n) upd(t[i],s[i]*i%MOD);
	LL ans=0;
	rep(i,1,n) ans=ans^t[i];
	printf("%lld\n", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/89341689