ARC077E guruguru 差分

~~~题面~~~

题解:

  首先考虑到原题是一个环(因为到m之后又会回到1),所以先破环成链,然后在后面把原来的复制一遍。

  那么对于每一个操作而言,都相当于从第一段的某个地方,向后走到达第一段or第二段的某个地方,没有回头。

  观察到不管我们的特殊按钮是哪个数,对于每次操作的开始和结束点都是不产生影响的,因此我们应该要单独考虑这个特殊按钮对每一组操作的贡献。

  那么对于从x到y这组操作,有哪些值为k的特殊按钮可以产生贡献呢?

  我们可以分两种情况讨论:

  1,x < y

  

  红色区域即为可以减小代价的k值,那么考虑对于每个k值而言,可以减小多少代价呢?

  当k = x + 2时,我们可以把2步变成1步,所以减小了代价1,;同理,对于k = x + 3,减小了代价2……以此类推,减小的代价是一个等差数列。

  所以我们只需要先差分一次,然后统计2次前缀和就可以实现O(1)的给一段区间加上一个等差数列。

  即1 0 0 0 0 0 -1 ----> 1 1 1 1 1 0 ----> 1 2 3 4 5 0

  为了在第6个位置把前面的累计贡献完全消除,我们还需要记录一个d[i]表示需要减小多少累积值,具体可以看代码,自己推一推也行。

  2,x > y

  维护方法和上一种类似,为了方便,直接给红色区域加上贡献,在最后得到整个数组后再统计一次,把k[i] += k[i + m];

  然后用最初的代价(不考虑特殊按钮) 减去max(k[i])即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 201000
 5 #define LL long long
 6 
 7 int n, m;
 8 LL ans;
 9 LL s[AC], k[AC], d[AC];
10 
11 inline int read()
12 {
13     int x = 0;char c = getchar();
14     while(c > '9' || c < '0') c = getchar();
15     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
16     return x;
17 }
18 
19 inline int cal(int l, int r)
20 {    
21     if(l == r) return 0;
22     if(l < r) return r - l;
23     else return r + m - l;
24 }
25 
26 inline void pre()
27 {
28     n = read(), m = read();
29     for(R i = 1; i <= n; i ++) 
30     {
31         s[i] = read();
32         if(i > 1) ans += cal(s[i - 1], s[i]);//先计算出没有特殊按钮的代价
33     }
34 }
35 
36 inline void add(int l, int r)
37 {
38     //printf("%d %d\n", l, r);
39     if(l > r) return ;
40     ++ k[l], -- k[r + 1], d[r + 1] -= r - l + 1;//是要在后面减掉前面累积的
41 }
42 
43 inline void upmax(LL &a, LL b)
44 {
45     if(b > a) a = b;
46 }
47 
48 void work()
49 {
50     for(R i = 1; i < n; i ++)
51     {
52         if(s[i] == s[i + 1]) continue;
53         if(s[i] < s[i + 1]) add(s[i] + 2, s[i + 1]);
54         else add(s[i] + 2, s[i + 1] + m);
55     }
56     int b = 2 * m; LL maxn = 0;
57     for(R i = 1; i <= b; i ++) k[i] += k[i - 1];
58     for(R i = 1; i <= b; i ++) k[i] += k[i - 1] + d[i];
59     for(R i = 1; i <= m; i ++) k[i] += k[i + m], upmax(maxn, k[i]);
60     printf("%lld\n", ans - maxn);
61 }
62 
63 int main()
64 {
65     freopen("in.in", "r", stdin);
66     pre();
67     work();
68     fclose(stdin);
69     return 0;
70 }
View Code

猜你喜欢

转载自www.cnblogs.com/ww3113306/p/9827791.html