中秋节yyds
作者:互联网
P8539 「Wdoi-2」来自地上的支援
为什么都在用线段树啊?
首先,我们发现一个数如果有一次没有被选,那它以后就永远不会被选上了,所以x被选k次的临界是它从x到x+k-1一直被选上,x+k-1>n就不合法了。
为了保证这个数在它自己的位置上被选中第一次,它需要比前面的修改后最大值大或相等(因为修改值和原值相等时选原值),继续修改下去,它自己的不断增加的修改值还要大于后面的每一个原值。
这里涉及到了修改值的预处理,先用没有任何变动的A进行题目中的操作,因为比较的只是相对大小,与其每次找出来最大的让它+v,不如先对于每一个数把v*i减掉,比较相对大小时就相当于和与它比较的数的修改值相比了。
所以就用线段树——前面的预处理,后面的最大值区间查询。
可是线段树是没有必要的。
我们发现要保持在1~x-1和x+1~x+k-1之内只要区间够大就要必选x,所以我们只需要一个前缀,f[i]表示在原来的A中截止到i最大的修改值的位置,就是他原来由于B最大什么的已经加上了若干个v,我们只需要让A比这个临界区间范围内最大可能的B大就好了。
分成4种情况:
前两种,如果这最大值就是x,那么它是无效的,因为x的初始值成了变量,它原来在A中的值是可行解但不是最优解,所以预处理的时候还要处理上次大值g,不用严格(可以相等),把这个g看做是最大值,如果它出现在x之前,修改值和它相等就可以了,出现在x之后就需要修改值一定比它大。x需要加上它和最大值的差值,如果最大值在它之前就加上正差距,否则加上负差距,在代码里的体现是x*v和A[maxpos]直接加,因为A都是减过的。
最大值不是x那直接用最大值f就可以了,不管次大值g什么事,其他的都一样。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e6 + 3; const int INF = 0x7fffffff; int n, m, X[maxn], K[maxn]; ll v, A[maxn], f[maxn], g[maxn], w, l; ll an2, an1; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int main() { n = read(); m = read(); v = read(); for(int i=1; i<=n; i++) { A[i] = read(); } for(int i=1; i<=m; i++) { X[i] = read(); K[i] = read(); } A[0] = -INF; for(int i=1; i<=n; i++) { A[i] = w = A[i] - i*v; if(w >= A[f[i-1]]) g[i] = f[i-1], f[i] = i; else if(w >= A[g[i-1]]) g[i] = i, f[i] = f[i-1]; else g[i] = g[i-1], f[i] = f[i-1]; } for(int i=1; i<=m; i++) { int x = X[i], k = K[i], y = x+k-1; l = 0; if(y < n) l = max(0ll, x*v+(f[y]==x?A[g[y]]+(g[y]>x):A[f[y]]+(f[y]>x))); an1 ^= l; an2 += l; } printf("%lld %lld\n", an1, an2); return 0; }View Code
标签:yyds,ch,中秋节,int,最大值,修改,maxn,ll 来源: https://www.cnblogs.com/Catherine2006/p/16683408.html