# D

# 题目大意

给你一个长度为 nn 的,只有 0,10,1 构成的字符串 ss,你可以执行以下操作若干次

  • 选择任意一个字符,Si1SiS_i \leftarrow 1-S_i

请问,至少经过多少次操作,字符串的 11 连续或不存在 11

# 数据范围

  • 1T2×1041 \le T \le 2 \times 10^4
  • 1S2×1051 \le |S| \le 2 \times 10^5

# 题解

如果最终,是 [l,r][l,r] 区间连续的 11 的话,其操作次数为

(rl+1(p[r]p[l1]))区间里要翻的 0 数+(p[l1]+(p[n]p[r]))区间外要翻的 1 数,\underbrace{\bigl(\,r-l+1 - \bigl(p[r]-p[l-1]\bigr)\bigr)}_{\text{区间里要翻的 0 数}} +\underbrace{\bigl(p[l-1] + (p[n]-p[r])\bigr)}_{\text{区间外要翻的 1 数}},

化简有

p[n]+(r2p[r])+(2p[l1](l1))p[n]+(r-2p[r])+(2p[l-1]-(l-1))

这样看来,还是有两个变量,枚举需要 n2n^2 时间,怎么办呢?
我们不放想想,我们求的是答案的最小值,那我们可以只遍历 rr,对于一个确定的 rr,其是不是只要找到,mini=1r1(2p[l1](l1))\min\limits_{i=1}^{r-1} (2p[l-1]-(l-1)) 呢,而这个我们就可以用前缀最小值来实现

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void solve () {
cin >> n >> s, s = " " + s;
vector<int> pre(n + 1, 0);
for (int i = 1; i <= n; i++)
pre[i] = pre[i - 1] + (s[i] == '1');

int pml = 0, res = 1e18;
for (int r = 1; r <= n; r++) {
pml = min(pml, 2*pre[r] - r);
int t = pre[n] + (r - 2*pre[r]) + pml;
res = min(res, t);
}

cout << res << '\n';
}

这题提示我们,如果出现了区间最小 / 最大操作次数的题目,我们不妨从暴力枚举 [l,r][l,r] 区间的地方开始着手
利用前缀和或其他知识,写出最终区间是 [l,r][l, r] 的操作数的表达式,可以猜想到,最终表达式一定是一个关于 [l,r][l, r] 的方程
此时不妨尝试枚举 rr,进而观察 ll 相关的式子,比如此题中,要求的是求最小值,而 ll 相关的式子可以用一个前缀最小值来简化掉一层循环

# E

# 题目大意

现有一个 nn 个点 mm 条边的无向连通图,请你求出,顶点 11 \to 顶点 nn 的路径中,所有边的权值 OROR 最小值

  • 可能存在重边

# 数据范围

  • 2N2×1052 \le N \le 2 \times 10^5
  • N1M2×105N - 1 \le M \le 2 \times 10^5
  • 0 \le w_i \le 2^

# 题解

C++
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
struct Dsu {
int f[N];
void init(int n) {
for(int i = 1; i <= n; i++)
f[i] = i;
}

int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}

void merge(int x, int y) {
f[find(x)] = find(y);
}
} dsu;

struct Side {
int u, v, w;
};

int n, m;

void solve () {
cin >> n >> m;
vector<Side> a(m + 1);

for (int i = 1; i <= m; i++)
cin >> a[i].u >> a[i].v >> a[i].w;

int res = (1 << 30) - 1;

for (int i = 29; i >= 0; i--) {
res -= (1 << i);

dsu.init(n);

for (int j = 1; j <= m; j++)
if ((res | a[j].w) == res)
dsu.merge(a[j].u, a[j].v);

if (dsu.find(1) != dsu.find(n))
res += (1 << i);
}

cout << res << '\n';
}

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

duoxichangan 微信支付

微信支付

duoxichangan 支付宝

支付宝