标签:
1、题意:给一个序列,枚举长度x,然后在这个序列中所有长度为x的区间,我们求出这些区间的最大值之和并取模,最后将所有的异或起来就好啦
2、分析:听说好多人写的
话说这个东西,我们对于每一个点,设这个点的值是然后我们就可以随便写写就A了 ,这明显是不能AC的,那我们考虑一个点对于每个长度的贡献,考虑这样的一个点
①当
②当
③当
那么我们分别来计算这三个函数对答案的贡献。
①:这个我们可以找出一个长度
②:这个是最水的一个,我们差分一下,然后求一下前缀和就好了QAQ。
③:这个貌似是最复杂的一个,还是打标记这种东西,我们发现这个分段函数单调递减,那么我们在递减开始的
最后友情提示,那个单调栈的判断一定要左边寻找比他大,右边寻找大于等于它的,这样相等的情况才能包括进去
解决啦,撒花!
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 1000010
#define LL long long
#define MOD 998244353
inline int read(){
char ch = getchar(); int x = 0, f = 1;
while(ch < ‘0‘ || ch > ‘9‘){
if(ch == ‘-‘) f = -1;
ch = getchar();
}
while(‘0‘ <= ch && ch <= ‘9‘){
x = x * 10 + ch - ‘0‘;
ch = getchar();
}
return x * f;
}
int a[M];
int ll[M];
int rr[M];
int z[M], tot;
LL ans[M], beta[M], lazy[M], plus[M], vfk[M];
LL cf[M];
int main(){
int n = read();
for(int i = 1; i <= n; i ++) a[i] = read();
for(int i = 1; i <= n; i ++){
while(tot > 0 && a[z[tot]] <= a[i]) tot --;
ll[i] = z[tot]; z[++ tot] = i;
}
tot = 0; z[0] = n + 1;
for(int i = n; i >= 1; i --){
while(tot > 0 && a[z[tot]] < a[i]) tot --;
rr[i] = z[tot]; z[++ tot] = i;
}
for(int i = 1; i <= n; i ++){
int l = i - ll[i] - 1;
int r = rr[i] - i - 1;
if(l > r) swap(l, r);
if(l != 0){
LL w = min(l, r);
lazy[w] += (LL)w * (LL)a[i];
beta[r + 2] += (LL)w * (LL)a[i];
//puts("fuck");
plus[r + 2] += a[i];
vfk[r + 2 + l] += a[i];
}
cf[l + 1] += (LL)(l + 1) * (LL)a[i];
cf[r + 2] -= (LL)(l + 1) * (LL)a[i];
}
for(int i = n + 1; i > 1; i --){
ans[i - 1] = ans[i] / (LL)(i) * (LL)(i - 1) + lazy[i - 1];
}
LL sum = 0ll, divt = 0ll;
for(int i = 1; i <= n; i ++){
sum += beta[i] - divt;
divt += plus[i];
divt -= vfk[i];
ans[i] += sum;
}
sum = 0ll;
for(int i = 1; i <= n; i ++){
sum += cf[i];
ans[i] += sum;
}
for(int i = 1; i <= n; i ++) ans[i] %= MOD;
LL res = 0ll;
for(int i = 1; i <= n; i ++){
res ^= ans[i];
}
printf("%lld\n", res);
return 0;
}
标签:
原文地址:http://blog.csdn.net/qzh_1430586275/article/details/51933931