G. Maximize the Remaining String
由小写字母组成的字符串 s s s,每次从中选取重复的字母进行删除,直到该字符串中的每个字母都只出现一次。问:最终形成的字典序最大的字符串,比如 a b a b a b ababab ababab,答案为 b a ba ba。
1 ≤ l e n ( s ) ≤ 200000 1 \leq len(s) \leq 200000 1≤len(s)≤200000
题解
记 s = a 1 a 2 a 3 ⋯ a n s=a_1a_2a_3\cdots a_n s=a1a2a3⋯an,最后的字符串为 s ′ s' s′,它的首字符在 s s s中对应位置为 p p p,显然 p p p之前的字符都被删除了,并且被删除的字符在 p p p位置之后都出现过,根据这个性质,二分枚举 p p p。假设子串 a 1 a 2 ⋯ a p a_1a_2\cdots a_p a1a2⋯ap中字典序最大的字符为 a i a_i ai:
- a i > a p a_i > a_p ai>ap,则字符 a i a_i ai作为 s ′ s' s′的首字符,更新 s = a i + 1 a i + 2 a i + 3 ⋯ a n s=a_{i + 1}a_{i+2}a_{i+3}\cdots a_n s=ai+1ai+2ai+3⋯an - { a i } \{a_i\} {ai},(减号表示删除前一个字符串中所有的后一个字符)
- a i = = a p a_i == a_p ai==ap,则字符 a i a_i ai作为 s ′ s' s′的首字符,更新 s = a p + 1 a p + 2 a p + 3 ⋯ a n s=a_{p + 1}a_{p+2}a_{p+3}\cdots a_n s=ap+1ap+2ap+3⋯an - { a p } \{a_p\} {ap}
- a i < a p a_i < a_p ai<ap,则字符 a p a_p ap作为 s ′ s' s′的首字符,更新 s = a p + 1 a p + 2 a p + 3 ⋯ a n s=a_{p + 1}a_{p+2}a_{p+3}\cdots a_n s=ap+1ap+2ap+3⋯an - { a p } \{a_p\} {ap}
当 a i > a p a_i > a_p ai>ap时,如果有多个 a i a_i ai,显然选择第一次出现的 a i a_i ai更优
通过不断的枚举 p p p确定 s ′ s' s′中的每个字符,且枚举次数不大于26次。
复杂度 O ( 26 ∗ n ∗ l o g ( n ) ) O(26*n*log(n)) O(26∗n∗log(n))
E. K-periodic Garland
由0,1组成的字符串 s s s,如果 s s s中任意相邻字符’1’之间的距离为 K K K,则该字符串是良好的。将 ‘0’ → \rightarrow → ‘1’ (或者 ‘1’ → \rightarrow → ‘0’) 称为一次操作,问最少几次操作后,能将 s s s变成良好的?
注意:‘00’、‘0100’ 对任意的 K K K都是良好的, 1 ≤ K ≤ l e n ( s ) ≤ 1 e 6 1 \le K \le len(s) \le 1e6 1≤K≤len(s)≤1e6
题解
记最终良好的字符串为 s t r str str,考虑 s s s中从左至右第一个 ‘1’ 出现的位置,记为 p o s pos pos,显然, s t r str str中从左至右第一个 ‘1’ 出现的位置不会小于 p o s pos pos,那么可以求得以 p o s pos pos作为 s t r str str中从左至右第一个 ‘1’ 出现的位置且满足良好的最少操作次数。如果不是 p o s pos pos,那么会不会是 s s s中第一个 ‘1’ 和 第二个 ‘1’ 之间的某个位置 P P P呢?假设 s t r str str满足上述条件,以 P P P位置作为 s t r str str中第一个 ‘1’ 出现的位置,这样做的话需要 t t t 次操作,而令 s [ p o s ] → 0 s[pos] \rightarrow 0 s[pos]→0,既让 s t r str str中第二个 ‘1’ 作为第一个 ‘1’,所需要的操作次数至多为 t − 1 t - 1 t−1,故这样的 P P P显然不应该考虑。综上所述,我们可以枚举 s t r str str的第一个’1’ 出现的位置,复杂度 O ( n 2 K ) O(\frac{n^2}{K}) O(Kn2)。对数论稍微敏感一点儿就会发现对 K K K的加法将这些位置划分为了 K K K类,什么意思呢?假设 K = 3 K = 3 K=3
i i i | |||||
---|---|---|---|---|---|
0 | 3 | 6 | 9 | 12 | 15 |
1 | 4 | 7 | 10 | 13 | 16 |
2 | 5 | 8 | 11 | 14 | 17 |
3 | 6 | 9 | 12 | 15 | 18 |
4 | 7 | 10 | 13 | 16 | 19 |
如果计算出了 p o s = 0 pos = 0 pos=0的答案,相当于也就计算出了 p o s = 3 , 6 , 9 , 12 pos = 3,6,9,12 pos=3,6,9,12的答案。所以只需要计算 K K K次,每次的复杂度为 O ( n K ) O(\frac{n}{K}) O(Kn),故总的复杂度为 O ( n ) O(n) O(n)。
//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,no-stack-protector,fast-math")
// cnt[j] := 以 j 位置作为 str 中第一个 ‘1’ 的位置且使得 s 变为 str 所需要的最少次数
#include <bits/stdc++.h>
#define IO ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 1e6 + 5;
int t, n, k;
int main()
{
cin >> t;
while (t--)
{
cin >> n >> k;
string s;
cin >> s;
vector<int> use(2 * n + 5, 0);
vector<int> cnt(2 * n + 5, 0);
int pos = -1;
for (int i = n - 1; ~i; i--) if (s[i] == '1') {
pos = i;
break;
}
if (pos == -1) {
cout << 0 << endl;
continue;
}
for (int i = 0; i < pos; i++) cnt[i] = -1;
use[n - 1] = (s[n - 1] == '1');
for (int i = n - 2; ~i; i--) {
if (s[i] == '1') use[i] = 1;
use[i] += use[i + 1];
}
int ans = n << 1 + 1;
for (int i = n - 1; ~i; i--) if (s[i] == '1') {
int tp = 0;
for (int j = i + k; ; j += k) {
tp += (use[j - k + 1] - use[j] + 1);
if (cnt[j] != -1) {
tp = tp - 1 + cnt[j];
if (use[i + 1]) tp = min(tp, use[i + 1]); // 直接抹掉 i 位置之后的所有的 ‘1’,可能较优。例如 K = 1,s = 10001
break;
}
if (j >= n) break;
}
cnt[i] = tp;
ans = min(ans, tp + use[0] - use[i]);
}
cout << ans << endl;
/* code */
}
return 0;
}
wonderful, BFS
给一个01矩阵 A m ∗ n A_{m*n} Am∗n,有 q q q次询问: x , y x,y x,y,问离 ( x , y ) (x,y) (x,y)最近的 1 的曼哈顿距离。
1 ≤ n , m ≤ 1000 1 \leq n,m \leq 1000 1≤n,m≤1000, 1 ≤ q ≤ 1 e 5 1 \leq q \leq 1e5 1≤q≤1e5
题解
加一个超级源点 s s s,与所有的 1 连接起来,然后以 s s s 作为起点,BFS遍历图的同时更新距离即可。复杂度 O ( n m ) O(nm) O(nm)【单纯的记录一哈,orz
queue < pair<int, int> > qe;
void BFS() {
memset(d, -1, sizeof(d));
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (mp[i][j]) {
d[i][j] = 0;
qe.push(make_pair(i, j));
}
while(!qe.empty()) {
pair<int, int> p = qe.front();
qe.pop();
for (int i = 0; i < 4; i++) {
int u = p.first + dx[i], v = p.second + dy[i];
if (u < 0 || v < 0 || u >= n || v >= m || d[u][v] != -1) continue;
d[u][v] = d[p.first][p.second] + 1;
qe.push(make_pair(u, v));
}
}
}
1349C - Orac and Game of Life【好题】
给一个长度为 n n n的序列,每次选择一个区间 [ l , r ] [l,r] [l,r]并将这个区间包含的元素都替换为该区间的中位数(按照大小关系排在中间的数)。问最终是否能将整个序列中的元素都替换为 k k k?
1 ≤ n ≤ 1 e 5 1 \leq n \leq 1e5 1≤n≤1e5, 1 ≤ a i , k ≤ 1 e 9 1 \leq a_i, k \leq 1e9 1≤ai,k≤1e9
题解
-
原始序列不包含 k k k,显然无解
-
如果原始序列中存在一个区间,其中位数为 k k k,则最终肯定能够将整个序列替换为 k k k。
-
如果原始序列不存在上述区间呢?构造
在原序列中直接找一个中位数为 k k k的区间,想了很久都没想到一个好的办法。其实,着眼于细微之处,如果序列中存在相邻的两个数为 k , a i k,a_i k,ai或者 a i , k a_i,k ai,k,有 a i ≥ k a_i \geq k ai≥k,那么显然这是一个中位数为 k k k的区间。如果不存在这样的相邻两个数,既对任意的 k k k,其左右相邻的两个数都小于 k k k( a i , k , a j a_i,k,a_j ai,k,aj,都有 a i < k a_i < k ai<k, a j < k a_j < k aj<k),那么只需要将其中的任意一个( a i a_i ai或者 a j a_j aj)替换为大于或者等于 k k k的数就行,等价于找一个中位数大于或者等于 k k k的区间。怎么找呢?【orz】其实只需要找到一个区间 ( a e , a e + 1 , a e + 2 ) (a_e,a_{e+1},a_{e+2}) (ae,ae+1,ae+2),其中至少存在两个大于 k k k的数就行。为什么只需要找长度为3区间呢?假设存在一个区间 [ l , r ] [l,r] [l,r]其中位数为 p p p, p ≥ k p\geq k p≥k,说明这个区间大于或者等于 p p p的元素至少有 r − l + 1 2 + 1 \frac{r-l+1}{2} + 1 2r−l+1+1个,既大于或者等于 k k k的数至少超过一半,显然,至少存在一个长度为3的区间其中位数必定大于或者等于 k k k。证明的话,将区间划分成一个一个长度为3且不相交的区间,然后对最后剩下的一个区间分类讨论即可。如果找不到上述的长度为3且满足条件的区间,显然不可能将 a i a_i ai或者 a j a_j aj替换为大于或者等于 k k k的数。
1349A - Orac and LCM
给一个长度为 n n n的序列,求 g c d ( { l c m ( a i , a j ) ∣ 1 ≤ i , j ≤ n , i ≠ j } ) gcd(\{lcm(a_i,a_j)|1 \leq i,j \leq n,i \neq j\}) gcd({lcm(ai,aj)∣1≤i,j≤n,i=j})
1 ≤ n , a i ≤ 2 e 5 1 \leq n,a_i \leq 2e5 1≤n,ai≤2e5
题解
想到了枚举最终答案的约数,但等于圈圈。记最终答案为 a n s ans ans, p p p表示质数,注意:如果 p k ∣ a n s p^k | ans pk∣ans,那么至少存在 n − 1 n-1 n−1个 a i a_i ai,有 p k ∣ a i p^k|a_i pk∣ai。
反证法很好证明。知道这个特点之后怎么做呢?直接的思路依然是枚举,然而!求 n − 1 n-1 n−1个 a i a_i ai的 g c d gcd gcd不就找到了 p k p^k pk吗,妙得一匹。记 g c d ( a i ) = g c d ( { a } − { a i } ) gcd(a_i) = gcd(\{a\} - \{a_i\}) gcd(ai)=gcd({a}−{ai}),那么 a n s = l c m ( g c d ( a 1 ) , g c d ( a 2 ) , g c d ( a 3 ) , ⋯ , g c d ( a n ) ) ans = lcm(gcd(a_1), gcd(a_2), gcd(a_3),\cdots,gcd(a_n)) ans=lcm(gcd(a1),gcd(a2),gcd(a3),⋯,gcd(an)).
1346F - Dune II: Battle For Arrakis
给一个棋盘 A n ∗ m A_{n*m} An∗m,每个格子上有 a i j a_{ij} aij枚棋子,记 c n t cnt cnt为 将所有的棋子移到同一个格子上所需要的最少次数,一次只能将一枚棋子移动一步(上下左右)。现给 k k k次修改:将 a i j → b i j a_{ij} \rightarrow b_{ij} aij→bij,问每次修改后需要的最少次数,既 c n t cnt cnt。
1 ≤ n , m , k ≤ 1000 1 \leq n,m,k \leq 1000 1≤n,m,k≤1000
题解
我的想法:研究修改后的最佳位置与先前最佳位置的关系,然并卵
记最佳位置为
(
x
,
y
)
(x,y)
(x,y),有
c
n
t
=
∑
i
=
1
n
∑
j
=
1
m
(
∣
x
−
i
∣
+
∣
y
−
j
∣
)
∗
a
i
j
=
(
∑
i
=
1
n
∑
j
=
1
m
∣
x
−
i
∣
∗
a
i
j
)
+
(
∑
i
=
1
n
∑
j
=
1
m
∣
y
−
j
∣
∗
a
i
j
)
=
(
∑
i
=
1
n
(
∣
x
−
i
∣
∗
∑
j
=
1
m
a
i
j
)
)
+
(
∑
j
=
1
m
(
∣
y
−
j
∣
∗
∑
i
=
1
n
a
i
j
)
)
\begin{aligned} cnt =& \sum_{i=1}^{n}\sum_{j=1}^m(|x-i|+|y-j|)*a_{ij} \\ =& (\sum_{i=1}^n\sum_{j=1}^m|x-i|*a_{ij}) + (\sum_{i=1}^n\sum_{j=1}^m|y-j|*a_{ij}) \\ =& (\sum_{i=1}^n(|x-i|*\sum_{j=1}^ma_{ij})) + (\sum_{j=1}^m(|y-j|*\sum_{i=1}^na_{ij})) \\ \end{aligned}
cnt===i=1∑nj=1∑m(∣x−i∣+∣y−j∣)∗aij(i=1∑nj=1∑m∣x−i∣∗aij)+(i=1∑nj=1∑m∣y−j∣∗aij)(i=1∑n(∣x−i∣∗j=1∑maij))+(j=1∑m(∣y−j∣∗i=1∑naij))
记
A
i
=
∑
j
=
1
m
a
i
j
A_i = \sum_{j=1}^ma_{ij}
Ai=∑j=1maij,
B
j
=
∑
i
=
1
n
a
i
j
B_j = \sum_{i=1}^na_{ij}
Bj=∑i=1naij,有
c
n
t
=
∑
i
=
1
n
∣
x
−
i
∣
∗
A
i
+
∑
j
=
1
m
∣
y
−
j
∣
∗
B
j
cnt = \sum_{i=1}^n|x-i|*A_i + \sum_{j=1}^m|y-j|*B_j
cnt=i=1∑n∣x−i∣∗Ai+j=1∑m∣y−j∣∗Bj
这是一个最优化问题,最小化
c
n
t
cnt
cnt,也就是分别最小化
∑
i
=
1
n
∣
x
−
i
∣
∗
A
i
\sum_{i=1}^n|x-i|*A_i
∑i=1n∣x−i∣∗Ai 和
∑
j
=
1
m
∣
y
−
j
∣
∗
B
j
\sum_{j=1}^m|y-j|*B_j
∑j=1m∣y−j∣∗Bj。
重点来了,最小化 ∑ i = 1 n ∣ x − i ∣ \sum_{i=1}^n|x-i| ∑i=1n∣x−i∣比较好做,在最中间任取一个数就行。那多了一部分 A i A_i Ai该怎么做喃?在脑海中抽象一个模型, x x x轴上有一些球落在 x i x_i xi处,记 x i x_i xi处的球的个数为 A i A_i Ai,然后有一个挡板 X X X也在 x x x轴上,问 X X X的最佳位置,使得 ∑ i = 1 n ∣ X − x i ∣ ∗ A i \sum_{i=1}^n|X-x_i|*A_i ∑i=1n∣X−xi∣∗Ai最小。本质上与最小化 ∑ i = 1 n ∣ x − i ∣ \sum_{i=1}^n|x-i| ∑i=1n∣x−i∣是同一个问题,所以 X X X的位置依然是在最中间。同样的道理可以求得 y y y的位置,知道 ( x , y ) (x,y) (x,y)后就可以以 O ( 1000 ) O(1000) O(1000)的复杂度求得 c n t cnt cnt,最终复杂度为 O ( m a x ( n , m ) ∗ k ) O(max(n,m)*k) O(max(n,m)∗k)
Palindrome(好题)
给一个字符串 s s s,问它有多少个子串是好子串?一个子串被称为好子串 S S S当且仅当它的长度为 3 n − 2 3n-2 3n−2并且 S [ i ] = S [ 2 n − i ] = S [ 2 n + i − 2 ] S[i] = S[2n-i] = S[2n + i - 2] S[i]=S[2n−i]=S[2n+i−2] ( 1 ≤ i ≤ n ) (1 \leq i \leq n) (1≤i≤n).
1 ≤ l e n ( s ) ≤ 500000 1 \leq len(s) \leq 500000 1≤len(s)≤500000
题解
分析 S [ i ] = S [ 2 n − i ] = S [ 2 n + i − 2 ] S[i] = S[2n-i] = S[2n + i - 2] S[i]=S[2n−i]=S[2n+i−2],可以发现对于一个长度为 3 n − 2 3n-2 3n−2的好子串 S S S,有:
- S 123... ( 2 n − 1 ) S_{123...(2n-1)} S123...(2n−1)是一个回文串并且中心为 n n n,半径为 n − 1 n-1 n−1;
- S n ( n + 1 ) ( n + 2 ) . . . ( 3 n − 2 ) S_{n(n+1)(n+2)...(3n-2)} Sn(n+1)(n+2)...(3n−2)是一个回文串并且中心为 2 n − 1 2n-1 2n−1,半径为 n − 1 n-1 n−1
枚举前一个回文串的中心和半径,那么后一个回文串的中心和半径也就确定了。先用马拉车算法预处理出每个位置的回文半径 l e n [ p o s ] len[pos] len[pos]。考虑已经枚举到 s s s的第 i i i位置,该位置的回文半径为 l e n [ i ] len[i] len[i],如果 l e n [ i + l e n [ i ] ] ≥ l e n [ i ] len[i+len[i]] \geq len[i] len[i+len[i]]≥len[i],那么一个好子串就出现了,所以需要考虑以 i i i位置为中心的所有回文子串,检查是否有合法的后一个回文串,既 l e n [ i + l e n [ i ] − t ] ≥ l e n [ i ] − t len[i + len[i] - t] \geq len[i] - t len[i+len[i]−t]≥len[i]−t ( 0 ≤ t ≤ l e n [ i ] − 1 ) (0 \leq t \leq len[i] - 1) (0≤t≤len[i]−1).
显然,不能直接暴力的检查 以
i
i
i位置为中心的所有回文子串是否有合法的后一个回文串。观察上面这个不等式,移项:
l
e
n
[
i
+
l
e
n
[
i
]
−
t
]
+
t
≥
l
e
n
[
i
]
(
0
≤
t
≤
l
e
n
[
i
]
−
1
)
len[i + len[i] - t] + t \geq len[i] ~~~~~~~~~~~~~~~(0 \leq t \leq len[i] - 1)
len[i+len[i]−t]+t≥len[i] (0≤t≤len[i]−1)
重点来了,令
p
[
i
]
=
n
−
i
+
1
p[i] = n - i + 1
p[i]=n−i+1,
(
1
≤
i
≤
n
)
(1\leq i \leq n)
(1≤i≤n),那么合法的后一个回文串的个数就等于在
∑
j
=
i
+
1
i
+
l
e
n
[
i
]
(
(
l
e
n
[
j
]
+
p
[
j
]
−
p
[
i
+
l
e
n
[
i
]
]
)
≥
l
e
n
[
i
]
)
\sum_{j=i+1}^{i+len[i]}((len[j] + p[j]-p[i+len[i]])\geq len[i])
∑j=i+1i+len[i]((len[j]+p[j]−p[i+len[i]])≥len[i]),移项:
∑
j
=
i
+
1
i
+
l
e
n
[
i
]
(
l
e
n
[
j
]
+
p
[
j
]
≥
p
[
i
+
l
e
n
[
i
]
]
+
l
e
n
[
i
]
)
\sum_{j = i + 1}^{i+len[i]}(len[j] + p[j] \geq p[i + len[i]] + len[i])
j=i+1∑i+len[i](len[j]+p[j]≥p[i+len[i]]+len[i])
求该和式等价于求一个区间
[
l
,
r
]
[l,r]
[l,r]内,大于或者等于
K
K
K的数有多少个?方法比较多,主席树,线段树,离线树状数组。
/* 树状数组 */
int n, m;
int len[maxn << 1], b[maxn];
char s[maxn << 1], s1[maxn];
pair<int, int> a[maxn];
void change() {
for (int i = 1, t = 0; i <= n; i++) {
s[++t] = '#';
s[++t] = s1[i];
}
s[0] = '$', s[2 * n + 1] = '#', s[2 * n + 2] = '@', s[2 * n + 3] = '\0';
n = 2 * n + 3;
}
void ma() {
int id = 0;
for (int i = 1; i <= n; i++) {
int t = i > len[id] + id ? t = 0 : min(len[2 * id - i], id + len[id] - i);
while(s[i + t] == s[i - t]) t++;
len[i] = t - 1;
if (id + len[id] <= i + len[i]) {
id = i;
}
}
id = 0;
for (int i = 1; i <= n; i++) if (s[i] != '#') {
len[++id] = len[i] / 2;
}
}
struct query {
int l, r, x;
bool operator < (const query& i) const {
if (x == i.x) return l < i.l;
return x < i.x;
}
} q[maxn];
int lowbit(int x) {
return x & (-x);
}
void add(int i, int x) {
while(i <= m) {
b[i] += x;
i += lowbit(i);
}
}
int sum(int i) {
int res = 0;
while(i > 0) {
res += b[i];
i -= lowbit(i);
}
return res;
}
void solve() {
int t = 0;
for (int i = 1; i <= m; i++) if (len[i]) {
q[t].l = i + 1;
q[t].r = i + len[i];
q[t++].x = len[i] + m - i - len[i] + 1;
}
for (int i = 1; i <= m; i++) {
a[i].first = len[i] + m - i + 1;
a[i].second = i;
}
sort(a + 1, a + m + 1);
sort(q, q + t);
int j = 1;
long long res = 0;
for (int i = 0; i < t; i++) {
while(a[j].first < q[i].x && j <= m) {
add(a[j].second, 1);
j++;
}
res += (q[i].r - q[i].l + 1) - (sum(q[i].r) - sum(q[i].l - 1));
}
printf("%lld\n", res);
}
int main()
{
int cas;
cin >> cas;
while (cas--)
{
/* code */
scanf("%s", s1 + 1);
memset(len, 0, sizeof(len));
memset(b, 0, sizeof(b));
m = n = strlen(s1 + 1);
change();
ma();
solve();
}
return 0;
}
1346E - Magic Tricks
n n n个位置,每个位置上都有一个球。初始时,第 k k k个位置上是一个特殊的球。现给出 m m m次交换,记 t i t_i ti表示 m m m次交换后,特殊的球被交换到了 i i i位置的最少 f a k e s w a p fake ~ swap fake swap 次数。一次交换被称为 f a k e s w a p fake ~ swap fake swap 当且仅当这次交换什么都没做,既不发生交换。求出所有 t i t_i ti ( 1 ≤ i ≤ n ) (1 \leq i \leq n) (1≤i≤n)。
1 ≤ n , m ≤ 2 e 5 1 \leq n,m \leq 2e5 1≤n,m≤2e5
题解
时间是线性的,令
d
p
[
a
]
[
b
]
:
=
dp[a][b]:=
dp[a][b]:=
b
b
b次交换后,特殊的球被交换到
a
a
a位置的最少
f
a
k
e
s
w
a
p
fake ~ swap
fake swap次数,有:
假
设
第
i
次
交
换
为
:
(
u
,
v
)
d
p
[
u
]
[
i
]
=
m
i
n
(
d
p
[
u
]
[
i
−
1
]
+
1
,
d
p
[
v
]
[
i
−
1
]
)
d
p
[
v
]
[
i
]
=
m
i
n
(
d
p
[
v
]
[
i
−
1
]
+
1
,
d
p
[
u
]
[
i
−
1
]
)
假设第i次交换为:(u,v)\\ dp[u][i] = min(dp[u][i - 1] + 1, dp[v][i - 1]) \\ dp[v][i] = min(dp[v][i - 1] + 1, dp[u][i - 1])
假设第i次交换为:(u,v)dp[u][i]=min(dp[u][i−1]+1,dp[v][i−1])dp[v][i]=min(dp[v][i−1]+1,dp[u][i−1])
1345D - Monopole Magnets
给一个 n ∗ m n*m n∗m的棋盘,每个格子都有一种颜色,黑色或者白色,然后将一些 N N N和 S S S放入棋盘的格子中,一个格子可以放入多个,且 N N N和 S S S可以放在同一个格子里面。求最少需要放入 N N N的个数( S S S的个数不限)使得满足如下三个条件:
- 棋盘的每行每列至少有一个 S S S。
- 对于黑色位置 ( i , j ) (i,j) (i,j),经过有限次操作后,至少存在一个 N N N能到达这些位置。
- 对于白色位置 ( i , j ) (i,j) (i,j),无论经过多少次的操作,都没有 N N N能到达这些位置。
一次操作:选取任意两个在同行或者同列,但不在同一个格子里面的 N N N和 S S S,保持 S S S的位置不变, N N N向 S S S靠近一步。
1 ≤ n , m ≤ 1000 1 \leq n,m \leq 1000 1≤n,m≤1000
题解
先判断不存在解的情况。如果 ( i , j ) (i,j) (i,j)位置是白色,且这个位置放 S S S,那么第 i i i行和第 j j j列不能存在黑色位置,因为既然能达到黑色位置,则经过有限次操作后,必定能到达 ( i , j ) (i,j) (i,j),而 ( i , j ) (i,j) (i,j)是不可达的。如果 ( i , j ) (i,j) (i,j)位置是黑色,且这个位置放 S S S,那么第 i i i行和第 j j j列只能存在一段连续的黑色位置,因为若存在两端或者更多段黑色位置,则段与段之间白色的部分必定可达。检查完所有的位置后,如果某行或者某列所有的位置都不能放 S S S,则解不存在。
经过上面的检查后,棋盘中黑色的位置组成了一个个连通块,在每个连通块的边界都放上 S S S,显然,一个连通块只需要在块内任意位置处放入一个 N N N就行了。故最少的 N N N的个数就是连通块的个数。
1343D - Constant Palindrome Sum
给一个长度为 n n n的序列 A A A,问最少需要几次操作使得 ∀ i ∈ [ 1 ; n 2 ] \forall i \in [1;\frac{n}{2}] ∀i∈[1;2n],有 a i + a n − i + 1 = X a_i + a_{n-i+1} = X ai+an−i+1=X。
一次操作:将任意一个数置为区间 [ 1 ; k ] [1;k] [1;k]内的数。
1 ≤ a i ≤ k ≤ 2 e 5 1 \leq a_i \leq k \leq 2e5 1≤ai≤k≤2e5, 1 ≤ n ≤ 2 e 5 1 \leq n \leq 2e5 1≤n≤2e5,且 n n n为偶数。
题解
记 x = m i n ( a i , a n − i + 1 ) x = min(a_i, a_{n - i + 1}) x=min(ai,an−i+1), y = m a x ( a i , a n − i + 1 ) y = max(a_i, a_{n - i + 1}) y=max(ai,an−i+1)。如果 X X X落在区间:
- [ 2 , x ] [2, x] [2,x],第 i i i对的贡献为2
- [ x + 1 , x + y − 1 ] [x + 1, x + y - 1] [x+1,x+y−1],第 i i i对的贡献为1
- [ x + y , x + y ] [x+y, x+y] [x+y,x+y],第 i i i对的贡献为0
- [ x + y + 1 , k + y ] [x + y + 1, k + y] [x+y+1,k+y],第 i i i对的贡献为1
- [ k + y + 1 , 2 ∗ k ] [k + y + 1, 2 * k] [k+y+1,2∗k],第 i i i对的贡献为2
显然,我们可以在每一对 i i i 所对应的这些区间上预先加上贡献,最后求区间 [ 2 ; 2 ∗ k ] [2; 2*k] [2;2∗k]上的最小值就是最少操作次数。区间操作,单点查询,可以用树状数组。但还有比较简单的方法,利用差分:在区间 [ l , r ] [l,r] [l,r]上加1,则 b [ l ] + = 1 b[l] += 1 b[l]+=1, b [ r + 1 ] − = 1 b[r + 1] -= 1 b[r+1]−=1,处理完所有的区间后,扫描一遍求前缀和,既 b [ i ] + = b [ i − 1 ] b[i] += b[i - 1] b[i]+=b[i−1],最后 a n s w e r = m i n { b i } answer = min\{b_i\} answer=min{bi}。
1342C - Yet Another Counting Problem
给两个正整数 a , b a,b a,b,问在区间 [ L , R ] [L,R] [L,R]内有多少个数满足: ( x % a ) % b ! = ( x % b ) % a (x \% a) \% b ~~!= ~~(x\%b)\%a (x%a)%b != (x%b)%a ?
1 ≤ a , b , L , R ≤ 1 e 18 1 \leq a, b, L, R \leq 1e18 1≤a,b,L,R≤1e18
题解
不妨假设 a ≤ b a \leq b a≤b,如果 ( x % a ) % b = ( x % b ) % a (x \% a) \% b ~~= ~~(x\%b)\%a (x%a)%b = (x%b)%a,有: x % a = ( x % b ) % a x \% a ~~= ~~(x\%b)\%a x%a = (x%b)%a,并且 a ∣ ( x − x % b ) a | (x - x \% b) a∣(x−x%b)。假设 x = p ∗ b + r x = p * b + r x=p∗b+r,则有 a ∣ ( p ∗ b ) a |(p*b) a∣(p∗b),既 p p p是 a g c d ( a , b ) \frac{a}{gcd(a,b)} gcd(a,b)a的倍数,既 p p p的取值为: { 0 , a g c d ( a , b ) , 2 a g c d ( a , b ) , 3 a g c d ( a , b ) , . . . } \{0, \frac{a}{gcd(a,b)}, \frac{2a}{gcd(a,b)}, \frac{3a}{gcd(a,b)},... \} {0,gcd(a,b)a,gcd(a,b)2a,gcd(a,b)3a,...},而 r r r的取值为: { 0 , 1 , 2 , 3 , . . . , b − 1 } \{0, 1, 2, 3, ...,b-1\} {0,1,2,3,...,b−1}。所以区间 [ 0 , R ] [0, R] [0,R]内满足相等条件的 x x x的个数为: ( R + 1 ) ∗ g c d ( a , b ) a b ∗ b + m i n ( b , ( R + 1 ) % ( a b g c d ( a , b ) ) ) \frac{(R+1)*gcd(a,b)}{ab} * b + min(b, (R+1)\%(\frac{ab}{gcd(a,b)})) ab(R+1)∗gcd(a,b)∗b+min(b,(R+1)%(gcd(a,b)ab))。
1342D - Multiple Testcases
给一个长度为 n n n的序列 A A A, 1 ≤ a i ≤ k 1 \leq a_i \leq k 1≤ai≤k,和长度为 k k k的序列 C C C, c 1 ≥ c 2 ≥ c 3 ≥ . . . ≥ c k c_1 \geq c_2 \geq c_3 \geq ... \geq c_k c1≥c2≥c3≥...≥ck,问 A A A最少能被分成几组,使得每组都满足如下条件:
- 记每组中大于或者等于 i i i 的个数为 g i g_i gi,有: g i ≤ c i g_i \leq c_i gi≤ci。
1 ≤ n , k ≤ 2 e 5 1 \leq n,k \le 2e5 1≤n,k≤2e5
题解
记最少组数为 a n s ans ans,有: a n s = m a x ⌈ g i c i ⌉ ans = max\lceil \frac{g_i}{c_i} \rceil ans=max⌈cigi⌉。怎么构造呢,令 a i ∈ a n s [ i % a n s ] a_i \in ans[i \% ans] ai∈ans[i%ans], i ∈ 0 , 1 , 2 , . . . , n − 1 i \in {0,1,2,...,n-1} i∈0,1,2,...,n−1。还是比较巧妙得!
1341D - Nastya and Scoreboard
给 n n n个数字显示器,问再打开 k k k个显示器中导管后,这 n n n个数字显示器所组成的最大整数?
1 ≤ n , k ≤ 2000 1 \leq n, k \leq 2000 1≤n,k≤2000
题解
令 d p [ i ] [ j ] : = dp[i][j]:= dp[i][j]:= i i i 个数字显示器再打开 j j j个导管时,第 i i i个数字显示器显示的最大数字。为了满足最大的整数,第一个显示器显示的数字应该尽可能的大,实际上就是 d p [ 0 ] [ k ] dp[0][k] dp[0][k]。从后向前递归,**记录所有合法的状态 d p [ i ] [ j ] dp[i][j] dp[i][j] **,再从前向后回溯就能找到所有合法的显示器的数字了。
string s[10] = {"1110111", "0010010", "1011101", "1011011", "0111010", "1101011", "1101111", "1010010", "1111111", "1111011"};
int get_int(string str) {
int res = 0;
for (int i = 6, j = 1; ~i; i--, j *= 2) if (str[i] == '1') res += j;
return res;
}
int main()
{
memset(dp, -1, sizeof(dp));
memset(mp, -1, sizeof(mp));
cin >> n >> k;
for (int i = 0; i < n; i++) {
string t;
cin >> t;
int x = get_int(t);
for (int j = 0; j < 10; j++) {
int y = get_int(s[j]);
if ((x & y) == x) mp[i][__builtin_popcount(x ^ y)] = j;
}
}
for (int i = 0; i < 8; i++) dp[n - 1][i] = mp[n - 1][i];
for (int i = n - 2; ~i; i--) {
for (int j = 0; j <= k; j++) if (dp[i + 1][j] >= 0){
for (int p = 0; p <= 7 && j + p <= k; p++) dp[i][j + p] = max(dp[i][j + p], mp[i][p]);
}
}
int cnt = 0;
for (int i = 0; i < n; i++) {
int u = dp[i][k - cnt];
cout << u;
if (u == -1) return 0;
for (int j = 0; j <= 7; j++) if (mp[i][j] == u) {
cnt += j;
break;
}
}
cout << endl;
return 0;
}
1338B - Edge Weight Assignment(好题:通过构造点权值来构造边权值)
给一颗树,为每条边赋一个权值(正整数),问最少和最多需要几个不同的正整数,使得任意一对叶子之间路径异或值为0,既有: l 1 w 0 v 1 w 1 v 2 w 2 . . . v i w i l 2 l_1w_0v_1w_1v_2w_2...v_iw_il_2 l1w0v1w1v2w2...viwil2, w 0 ⨁ w 1 ⨁ w 2 ⨁ . . . ⨁ w i = 0 w_0 \bigoplus w_1 \bigoplus w_2 \bigoplus ... \bigoplus w_i = 0 w0⨁w1⨁w2⨁...⨁wi=0。
题解
最少需要1个或者3个,也就是对所有的边赋值同一个整整数,分类讨论一下即可。
为每个叶子节点赋点权值:0,为非叶节点赋点权值: 2 i 2^i 2i。则边权值为: w ( e ( i , j ) ) = v i ⨁ v j w(e(i,j)) = v_i \bigoplus v_j w(e(i,j))=vi⨁vj。显然,任意一对叶子之间的路径异或为0,所以最多需要 e − ∑ v ( l e a f ( v ) − 1 ) e - \sum_v(leaf(v) - 1) e−∑v(leaf(v)−1)个正整数。简直妙级了!
l e a f ( v ) leaf(v) leaf(v)表示节点 v v v的叶儿子个数.
1336A - Linova and Kingdom
给一颗 n n n个节点的树,根为1,从树中选取 k k k个节点建立工厂,剩余的所有节点建立公园,问这 k k k个工厂各自派出一个工人,这些工人走到树根最多能游玩几个公园?工人只能沿着最短路走。
1 ≤ k ≤ n ≤ 2 e 5 1 \leq k \leq n \leq 2e5 1≤k≤n≤2e5
题解
当一个节点被选择建立工厂时,它的所有子节点在之前就已经被选择建立工厂,否则具有更优的方案。所以每个节点的实际贡献为 d e p [ u ] − s i z e [ u ] dep[u] - size[u] dep[u]−size[u]。求出每个点的实际贡献,排个序,从大往小选择 k k k个贡献较大的节点。
1333C - Eugene and an array
给一个序列,问有多少个子串满足:子串的任意一个子串都是好子串 ?
- 好子串 A l . . . r A_{l...r} Al...r: a l + a l + 1 + a l + 2 + . . . + a [ r ] ≠ 0 a_l + a_{l +1} + a_{l + 2} + ... + a[r] \neq 0 al+al+1+al+2+...+a[r]=0
1 ≤ n ≤ 2 e 5 1 \leq n \leq 2e5 1≤n≤2e5, − 1 0 9 ≤ a i ≤ 1 0 9 -10^9 \leq a_i \leq 10^9 −109≤ai≤109
题解
令 R [ i ] R[i] R[i]表示以 i i i为左端点且最短 非好子串 的右端点位置,既 a i + a i + 1 + a i + 2 + . . . + a R [ i ] = 0 a_i + a_{i + 1} + a_{i + 2} + ... + a_{R[i]} = 0 ai+ai+1+ai+2+...+aR[i]=0。当然,也可能不存在 R [ i ] R[i] R[i]。记 L [ R [ i ] ] = i L[R[i]] = i L[R[i]]=i,那么 i i i位置为左端点的好子串个数就是: R [ j ] − i R[j] - i R[j]−i 或者是 n − i + 1 n - i + 1 n−i+1, j = ( a r g m i n j R [ j ] ≥ i ) & & ( L [ R [ j ] ] ≥ i ) j = (argmin_jR[j] \geq i) \&\& (L[R[j]] \geq i) j=(argminjR[j]≥i)&&(L[R[j]]≥i)。 j j j 的取值应该使得 R [ j ] R[j] R[j]尽量靠近 i i i的同时其左边界要大于等于 i i i。
怎么求 R [ i ] R[i] R[i]?求出所有位置的前缀和 s u m [ i ] sum[i] sum[i],显然有 s u m [ R [ i ] ] = s u m [ i − 1 ] sum[R[i]] = sum[i-1] sum[R[i]]=sum[i−1],用一个二维数组记录相同 s u m sum sum值出现的所有位置,然后二分查找满足等式且最小的 R [ i ] R[i] R[i]就可以了。