标签:scanf include ring highlight 空格 for clu 维护 第一个
题目描述
输入
输出
样例输入
3
1
61
5
1 2 3 4 5
5
10187 17517 24636 19706 18756
样例输出
3721
148
821283048
题解
单调栈
区间异或和比较容易处理,关键在于区间最大值
考虑一个数作为最大值的贡献:使用单调栈处理出一个数左边第一个大于等于它的数的位置lp和右边第一个大于它的数的位置rp。那么该数的贡献为:左端点[lp[i]+1,i],右端点[i,rp[i]-1]。
然后再考虑异或和:区间异或和可以由前缀异或来表示。所以满足条件的区间的异或相当于suml在[lp[i],i-1],sumr在[i,rp[i]-1]的两个数的异或。
我们可以拆位,然后对于前缀异或和的某一位维护前缀1的个数。如果该为异或为1,则说明左边为1,右边为0或左边为0,右边为1。分别把方案数计算出来即可。
注意在计算suml所在区间的前缀相减时lp[i]-1可能为负数,因此需要把数组下标平移1位处理。
#include <cstdio> #include <cstring> #define N 100010 #define mod 1000000061 typedef long long ll; int a[N] , sum[N] , c[N][30] , lp[N] , rp[N] , sta[N] , tot; int main() { int T; scanf("%d" , &T); while(T -- ) { int n , i , j , ans = 0; scanf("%d" , &n); memset(c , 0 , sizeof(c)); for(i = 2 ; i <= n + 1 ; i ++ ) { scanf("%d" , &a[i]) , sum[i] = sum[i - 1] ^ a[i]; for(j = 0 ; j < 30 ; j ++ ) c[i][j] = c[i - 1][j] + (bool)(sum[i] & (1 << j)); } tot = 0 , sta[0] = 1; for(i = 2 ; i <= n + 1 ; i ++ ) { while(tot && a[sta[tot]] < a[i]) tot -- ; lp[i] = sta[tot] , sta[++tot] = i; } tot = 0 , sta[0] = n + 2; for(i = n + 1 ; i >= 2 ; i -- ) { while(tot && a[sta[tot]] <= a[i]) tot -- ; rp[i] = sta[tot] , sta[++tot] = i; } for(i = 2 ; i <= n + 1 ; i ++ ) for(j = 0 ; j < 30 ; j ++ ) ans = (ans + ((ll)(c[i - 1][j] - c[lp[i] - 1][j]) * (rp[i] - i - c[rp[i] - 1][j] + c[i - 1][j]) + (ll)(i - lp[i] - c[i - 1][j] + c[lp[i] - 1][j]) * (c[rp[i] - 1][j] - c[i - 1][j])) % mod * (1 << j) % mod * a[i]) % mod; printf("%d\n" , ans); } return 0; }
标签:scanf include ring highlight 空格 for clu 维护 第一个
原文地址:http://www.cnblogs.com/GXZlegend/p/7660204.html