题目传送门
传送门
题目大意
给定一个数组$a$和数组$b$,每次操作可以选择$a$的一个子区间将其中的数在模4意义下加1,问把$a$变成$b$的最少操作次数。
首先求$b - a$,再差分,令这个数组为$c$。
那么操作的次数是这个数组$c$中正数的和。
现在可以做的是选择一个地方+4,它后面的某个地方-4。
考虑什么时候可以使得当前的答案减少。
- 在$-3$处$+4$,在$3$处$-4$,答案减少2
- 在$-3$处$+4$,在$2$处$-4$,答案减少1
- 在$-2$处$+4$,在$3$处$-4$,答案减少1
首先我们将尽量靠后的$-3$和$3$匹配,然后将对它操作。
这个用一个栈就能做。
剩下就贪心一下就好了。注意当$-3$和$2$操作时会多出$-2$。
Code
1 /** 2 * bzoj 3 * Problem#3704 4 * Accepted 5 * Time: 1400ms 6 * Memory: 128316k 7 */ 8 #include9 #include 10 #include 11 #include 12 #include 13 using namespace std; 14 typedef bool boolean; 15 16 typedef class Input { 17 protected: 18 const static int limit = 65536; 19 FILE* file; 20 21 int ss, st; 22 char buf[limit]; 23 public: 24 25 Input():file(NULL) { }; 26 Input(FILE* file):file(file) { } 27 28 void open(FILE *file) { 29 this->file = file; 30 } 31 32 void open(const char* filename) { 33 file = fopen(filename, "r"); 34 } 35 36 char pick() { 37 if (ss == st) 38 st = fread(buf, 1, limit, file), ss = 0;//, cerr << "str: " << buf << "ed " << st << endl; 39 return buf[ss++]; 40 } 41 }Input; 42 43 #define digit(_x) ((_x) >= '0' && (_x) <= '9') 44 45 Input& operator >> (Input& in, unsigned& u) { 46 char x; 47 while (~(x = in.pick()) && !digit(x)); 48 for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0'); 49 return in; 50 } 51 52 Input& operator >> (Input& in, unsigned long long& u) { 53 char x; 54 while (~(x = in.pick()) && !digit(x)); 55 for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0'); 56 return in; 57 } 58 59 Input& operator >> (Input& in, int& u) { 60 char x; 61 while (~(x = in.pick()) && !digit(x) && x != '-'); 62 int aflag = ((x == '-') ? (x = in.pick(), -1) : (1)); 63 for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0'); 64 u *= aflag; 65 return in; 66 } 67 68 Input& operator >> (Input& in, long long& u) { 69 char x; 70 while (~(x = in.pick()) && !digit(x) && x != '-'); 71 int aflag = ((x == '-') ? (x = in.pick(), -1) : (1)); 72 for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0'); 73 u *= aflag; 74 return in; 75 } 76 77 Input& operator >> (Input& in, char* str) { 78 for (char x; ~(x = in.pick()) && x != '\n' && x != ' '; *(str++) = x); 79 return in; 80 } 81 82 Input in (stdin); 83 84 template 85 void pfill(T* pst, const T* ped, T val) { 86 for ( ; pst != ped; *(pst++) = val); 87 } 88 89 int n; 90 int *a, *b, *c; 91 boolean *used; 92 93 inline void init() { 94 in >> n; 95 a = new int[(n + 1)]; 96 b = new int[(n + 1)]; 97 c = new int[(n + 1)]; 98 for (int i = 1; i <= n; i++) { 99 in >> a[i]; 100 } 101 for (int i = 1; i <= n; i++) { 102 in >> b[i]; 103 c[i] = (b[i] - a[i] + 4) % 4; 104 } 105 } 106 107 stack<int> sta; 108 inline void solve() { 109 int ans = 0; 110 for (int i = n; i > 1; i--) 111 c[i] = c[i] - c[i - 1]; 112 used = new boolean[(n + 1)]; 113 pfill(used, used + n + 1, false); 114 for (int i = 1; i <= n; i++) 115 if (c[i] > 0) 116 ans += c[i]; 117 for (int i = 1, y; i <= n; i++) 118 if (c[i] == -3) 119 sta.push(i); 120 else if (c[i] == 3 && !sta.empty()) { 121 y = sta.top(); 122 sta.pop(); 123 used[y] = used[i] = true; 124 ans -= 2; 125 } 126 127 int have_2 = 0, have_3 = 0; 128 for (int i = 1; i <= n; i++) { 129 if (used[i]) 130 continue; 131 switch (c[i]) { 132 case 3: 133 if (have_3) 134 have_3--, ans--; 135 else if (have_2) 136 have_2--, ans--; 137 break; 138 case 2: 139 if (have_3) 140 have_3--, have_2++, ans--; 141 break; 142 case -2: 143 have_2++; 144 break; 145 case -3: 146 have_3++; 147 break; 148 } 149 } 150 printf("%d\n", ans); 151 } 152 153 int main() { 154 init(); 155 solve(); 156 return 0; 157 }