「JOI2017Final」绳

所以标题到底应不应该空格啊

题意简述

LOJ 2336

一根绳子,长为 $n$,初始绳子上每个单位长度有一个颜色。
可以把绳子沿两个单位长度之间折起来,但要求折起来后对应的位置颜色相同。
可以花费该单位长度绳子厚度的代价对某个单位长度染任意色。
对于每种初始在绳子上的颜色,求将绳子折起来后能够只剩两个单位长度且有位置为该颜色的最小代价。
$n\le 2\times 10^6$。

主要思路

显然先染色不劣(有厚度影响),并且最开始染色后只会保留两种颜色。
发现折绳子等价于将边上的颜色连续段长度减少 1,或者把对称点所在的连续段长度减半并将其到边上的绳子删去(即其变到边上)。
所以初始状态必须除了边上的颜色连续段长度都为偶数,容易证明其充分性。
于是对于某种需要保留的颜色,枚举最左边颜色连续段的奇偶性,即可知道每个该颜色连续段是否要向左右伸展一格
然后在剩下的位置中找个众数留下来即可。
通过 $O(1)$ 删加点 $O(1)$ 求众数的 trick 就可以做到 $O(n)$。

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <bits/stdc++.h>
namespace my_std {
using namespace std;
#define reg register
#define Rint register int
#define FOR(i, a, b) for (register int i = (a), ed_##i = (b); i <= ed_##i; ++i)
#define ROF(i, a, b) for (register int i = (a), ed_##i = (b); i >= ed_##i; --i)
#define Templ(T) template <typename T>
Templ(_Tp) inline int chkmin(_Tp &x, _Tp y) { return x > y ? x = y, 1 : 0; }
Templ(_Tp) inline int chkmax(_Tp &x, _Tp y) { return x < y ? x = y, 1 : 0; }
} // namespace my_std
using namespace my_std;

#define MMR (1 << 23)
struct INPUT{
char buf[MMR], *s, *t;
INPUT(){ s = buf, fread(buf, 1, MMR, stdin); }
inline INPUT& operator >>(int &x){
x = 0;
while(*s < 48) ++s;
while(*s > 47) x = x * 10 + *s++ - 48;
return *this;
}
}fin;
struct OUTPUT{
char buf[MMR], *s;
OUTPUT(){ s = buf; }
inline OUTPUT& operator <<(int x){
static int tmp[10];
Rint *top = tmp;
do{
*++top = (x % 10) ^ 48, x /= 10;
}while(x);
while(top != tmp) *s++ = *top--;
*s++ = 10;
return *this;
}
~OUTPUT(){ fwrite(buf, 1, s - buf, stdout); }
}fout;

#define N 1000010
int n, m;
int a[N], c[N];
int fix[N];
vector<int> v[N];
int ans[N];

struct DS{
int b[N], g;
inline void add(reg const int p){
--b[c[p]];
if(c[p] == g) ++g;
++b[++c[p]];
}
inline void del(reg const int p){
--b[c[p]];
if(c[p] == g && !b[c[p]]) --g;
++b[--c[p]];
}
}B;

inline void sol(){
FOR(p, 1, m){
reg const int C(c[p]);
FOR(i, 1, C) B.del(p);
for(Rint i: v[p]){
if(fix[i] && a[fix[i]] != p){
B.del(a[fix[i]]);
}
}
chkmin(ans[p], n - C - B.g);
for(Rint i: v[p]){
if(fix[i] && a[fix[i]] != p){
B.add(a[fix[i]]);
}
}
FOR(i, 1, C) B.add(p);
}
}

int main() {
fin >> n >> m;
FOR(i, 1, n){
fin >> a[i];
v[a[i]].push_back(i);
}
if(m == 1) return fout << 0, 0;
FOR(i, 1, m) ans[i] = n;
*B.b = m;
FOR(i, 1, n) B.add(a[i]);
FOR(i, 1, n) fix[i] = ((i - 1) ^ 1) + 1;
if(n & 1) fix[n] = 0;
sol();
fix[1] = 0;
FOR(i, 2, n) fix[i] = i ^ 1;
if(!(n & 1)) fix[n] = 0;
sol();
FOR(i, 1, m) fout << ans[i];
return 0;
}