[Codeforces] combinatorics (R1200) Part.2
题单:https://codeforces.com/problemset?tags=combinatorics,0-1200
1543B. Customising the Track
原题指路:https://codeforces.com/problemset/problem/1543/B
题意
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an.现有操作:选择两个下标 i , j ( 1 ≤ i , j ≤ n , i ≠ j ) i,j\ \ (1\leq i,j\leq n,i\neq j) i,j (1≤i,j≤n,i=j),令 a i − − , a j + + a_i--,a_j++ ai−−,aj++.求经过若干次(可能为 0 0 0次)操作后 ∑ i = 1 n ∑ j = i + 1 n ∣ a i − a j ∣ \displaystyle\sum_{i=1}^n \sum_{j=i+1}^n |a_i-a_j| i=1∑nj=i+1∑n∣ai−aj∣的最小值.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n (1≤n≤2e5).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 0 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (0\leq a_i\leq 1\mathrm{e}9) a1,⋯,an (0≤ai≤1e9).数据保证所有测试数据的 n n n之和不超过 2 e 5 2\mathrm{e}5 2e5.
思路
a n s = ∑ i = 1 n ∑ j = i + 1 n ∣ a i − a j ∣ \displaystyle ans=\sum_{i=1}^n \sum_{j=i+1}^n |a_i-a_j| ans=i=1∑nj=i+1∑n∣ai−aj∣描述序列 a [ ] a[] a[]的波动情况,欲使其最小,应尽量让 a [ ] a[] a[]均匀分布.设 a [ ] a[] a[]中元素之和为 s u m sum sum,则 n n n个元素每个先分 ⌊ s u m n ⌋ \left\lfloor\dfrac{sum}{n}\right\rfloor ⌊nsum⌋,这一部分不会对 a n s ans ans有贡献.剩下的 s u m % n sum\% n sum%n放在前 s u m % n sum\% n sum%n个元素上,此时前 s u m % n sum\% n sum%n个为 1 1 1的元素会与后面的 ( n − s u m % n ) (n-sum\% n) (n−sum%n)个为 0 0 0的元素每对产生 1 1 1点贡献,故 a n s = ( s u m % n ) ( n − s u m % n ) ans=(sum\% n)(n-sum\% n) ans=(sum%n)(n−sum%n).
代码
void solve() {
int n; cin >> n;
ll sum = 0;
for (int i = 0; i < n; i++) {
int a; cin >> a;
sum += a;
}
cout << (ll)(sum % n) * (n - sum % n) << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1574B. Combinatorics Homework
原题指路:https://codeforces.com/problemset/problem/1574/B
题意 ( 2 s 2\ \mathrm{s} 2 s)
给定四个整数 a , b , c , m a,b,c,m a,b,c,m,判断是否存在满足下列条件的字符串:①包含 a a a个字符’A’, b b b个字符’B’, c c c个字符’C’;②不存在其他字符;③有恰好 m m m对相邻相同的字符,若存在则输出"YES";否则输出"NO".
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据输入四个整数 a , b , c , m ( 1 ≤ a , b , c ≤ 1 e 8 , 0 ≤ m ≤ 1 e 8 ) a,b,c,m\ \ (1\leq a,b,c\leq 1\mathrm{e}8,0\leq m\leq 1\mathrm{e}8) a,b,c,m (1≤a,b,c≤1e8,0≤m≤1e8).
思路
考虑对给定的 a , b , c a,b,c a,b,c,求合法的 m m m的范围.
①最大值:显然将所有’A’、‘B’、'C’分别放一起时取得最大值,此时 m = ( a − 1 ) + ( b − 1 ) + ( c − 1 ) = a + b + c − 3 m=(a-1)+(b-1)+(c-1)=a+b+c-3 m=(a−1)+(b−1)+(c−1)=a+b+c−3.
②最小值:因字符’A’、‘B’、'C’地位等价,不妨设 a ≤ b ≤ c a\leq b\leq c a≤b≤c.
考虑构造"CACACA…CBCBCB…CCC…"的字符串,即用 ( a + b ) (a+b) (a+b)个字符’C’隔开相邻的’A’和’B’,
此时剩下的 c − ( a + b ) c-(a+b) c−(a+b)个字符’C’会组成 ( c − a − b − 1 ) (c-a-b-1) (c−a−b−1)对相邻相同的字符.
综上, m ∈ [ c − a − b − 1 , a + b + c − 3 ] m\in[c-a-b-1,a+b+c-3] m∈[c−a−b−1,a+b+c−3].
m m m可取到 [ c − a − b − 1 , a + b + c − 3 ] [c-a-b-1,a+b+c-3] [c−a−b−1,a+b+c−3]中的任意整数.
[证] 当 m > 0 m>0 m>0时,取走一个出现次数最多的字符,并使得 m − − m-- m−−,直至 m = 0 m=0 m=0.
考虑将取走的字符放回 m = 0 m=0 m=0的字符串中对应字符最后一次出现的位置之后,
显然这样使得该字符的出现次数 + + ++ ++,同时 m + + m++ m++.
代码
void solve() {
int tmpa, tmpb, tmpc, m; cin >> tmpa >> tmpb >> tmpc >> m;
int a = min({tmpa, tmpb, tmpc}), c = max({tmpa, tmpb, tmpc}), b = tmpa + tmpb + tmpc - a - c;
int low = c - a - b - 1, high = a + b + c - 3;
cout << (low <= m && m <= high ? "YES" : "NO") << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1581A. CQXYM Count Permutations
原题指路:https://codeforces.com/problemset/problem/1581/A
题意
求满足如下条件的长度为 2 n 2n 2n的 1 ∼ 2 n 1\sim 2n 1∼2n的排列的个数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模:满足 p i < p i + 1 p_i<p_{i+1} pi<pi+1的下标 i i i的个数不少于 n n n.
有 t t t组测试数据.每组测试数据输入一个整数 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5).数据保证所有测试数据的 n n n之和不超过 1 e 5 1\mathrm{e}5 1e5.
思路
称一个排列的值为满足 p i < p i + 1 p_i<p_{i+1} pi<pi+1的下标 i i i的个数.将一个值为 i i i的排列倒序可得值为 ( 2 n − i ) (2n-i) (2n−i)的排列,则所有值 < n <n <n的排列构成的集合与所有值 ≤ n \leq n ≤n的排列构成的集合间存在双射,则答案为每个集合的大小,即 a n s = 1 2 ⋅ ( 2 n ) ! ans=\dfrac{1}{2}\cdot (2n)! ans=21⋅(2n)!.
代码
const int MAXN = 2e5 + 5;
const int MOD = 1e9 + 7;
int fac[MAXN], ifac[MAXN];
void init() {
fac[0] = 1;
for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;
ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;
}
void solve() {
init();
CaseT {
int n; cin >> n;
cout << (ll)fac[2 * n] * qpow(2, MOD - 2, MOD) % MOD << endl;
}
}
int main() {
solve();
}
1582B. Luntik and Subsequences
原题指路:https://codeforces.com/problemset/problem/1582/B
题意
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an,令 s = ∑ i = 1 n a i \displaystyle s=\sum_{i=1}^n a_i s=i=1∑nai.称 a [ ] a[] a[]的一个非空子列是好的,如果其中的元素之和为 s − 1 s-1 s−1,求 a [ ] a[] a[]的非空子列中好的子列的个数.
有 t ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t (1≤t≤1000)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 60 ) n\ \ (1\leq n\leq 60) n (1≤n≤60).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 0 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (0\leq a_i\leq 1\mathrm{e}9) a1,⋯,an (0≤ai≤1e9).
思路
先不考虑 a [ ] a[] a[]中值为 0 0 0的元素,显然一个子列是好的当且仅当它是 a [ ] a[] a[]删除一个值为 1 1 1的元素得到的子列,而值为 0 0 0的元素可删可不删,设值为 0 0 0、 1 1 1的元素的个数分别为 c n t 0 cnt_0 cnt0、 c n t 1 cnt_1 cnt1,则 a n s = c n t 1 ⋅ 2 c n t 0 ans=cnt_1\cdot 2^{cnt_0} ans=cnt1⋅2cnt0.
代码
void solve() {
int n; cin >> n;
int cnt0 = 0, cnt1 = 0;
vi a(n);
for (auto& ai : a) {
cin >> ai;
cnt0 += ai == 0, cnt1 += ai == 1;
}
cout << (ll)cnt1 * ((ll)1 << cnt0) << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1658B. Marin and Anti-coprime Permutation
原题指路:https://codeforces.com/problemset/problem/1658/B
题意
定义一个长度为 n n n的排列 p 1 , ⋯ , p n p_1,\cdots,p_n p1,⋯,pn是好的,如果 gcd ( 1 ⋅ p 1 , 2 ⋅ p 2 , ⋯ , n ⋅ p n ) > 1 \gcd(1\cdot p_1,2\cdot p_2,\cdots,n\cdot p_n)>1 gcd(1⋅p1,2⋅p2,⋯,n⋅pn)>1.求 1 ∼ n 1\sim n 1∼n的排列中好的排列的个数,答案对 998244353 998244353 998244353取模.
有 t ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t (1≤t≤1000)组测试数据.每组测试数据输入一个 n ( 1 ≤ n ≤ 1000 ) n\ \ (1\leq n\leq 1000) n (1≤n≤1000).
思路
令 g = gcd ( 1 ⋅ p 1 , 2 ⋅ p 2 , ⋯ , n ⋅ p n ) g=\gcd(1\cdot p_1,2\cdot p_2,\cdots,n\cdot p_n) g=gcd(1⋅p1,2⋅p2,⋯,n⋅pn),则 g ≤ 2 g\leq 2 g≤2.
[证] ① g > 2 g>2 g>2时,若 ∃ \exists ∃素数 p > 2 s . t . p ∣ g p>2\ s.t.\ p\mid g p>2 s.t. p∣g,因 1 ∼ n 1\sim n 1∼n中有 ⌊ n p ⌋ \left\lfloor\dfrac{n}{p}\right\rfloor ⌊pn⌋个 p p p的倍数,则至多能匹配 2 ⌊ n p ⌋ < n 2\left\lfloor\dfrac{n}{p}\right\rfloor<n 2⌊pn⌋<n个数.
② g ≤ 2 g\leq 2 g≤2时,可将值为奇数的数放在偶数下标的位置,将值为偶数的数放在奇数下标的位置. 1 ∼ n 1\sim n 1∼n中有 n 2 \dfrac{n}{2} 2n个奇数和 n 2 \dfrac{n}{2} 2n个偶数,故 a n s = [ ( n 2 ) ! ] 2 ans=\left[\left(\dfrac{n}{2}\right)!\right]^2 ans=[(2n)!]2.
代码
const int MAXN = 1005;
const int MOD = 998244353;
int fac[MAXN], ifac[MAXN];
void init() {
fac[0] = 1;
for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;
ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;
}
void solve() {
init();
CaseT {
int n; cin >> n;
cout << (n & 1 ? 0 : (ll)fac[n / 2] * fac[n / 2] % MOD) << endl;
}
}
int main() {
solve();
}
1674B. Dictionary
原题指路:https://codeforces.com/problemset/problem/1674/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
将两相异的小写英文字母组成一个单词,将所有单词按字典序排序,下标从 1 1 1开始.给定一个单词,求其下标.
有 t ( 1 ≤ t ≤ 650 ) t\ \ (1\leq t\leq 650) t (1≤t≤650)组测试数据.每组测试数据输入一个长度为 2 2 2的、只包含两相异的小写英文字母的单词.
思路
预处理出所有单词即可.
代码
umap<string, int> mp;
void init() {
for (char fi = 'a'; fi <= 'z'; fi++) {
for (char se = 'a'; se <= 'z'; se++) {
if (fi == se) continue;
string word(2, ' ');
word[0] = fi, word[1] = se;
mp[word] = mp.size() + 1;
}
}
}
void solve() {
init();
CaseT{
string s; cin >> s;
cout << mp[s] << endl;
}
}
int main() {
solve();
}
1674C. Infinite Replacement
原题指路:https://codeforces.com/problemset/problem/1674/C
题意 ( 2 s 2\ \mathrm{s} 2 s)
给定一个只包含字符’a’的字符串 s s s和只包含小写英文字母的字符串 t t t.现有操作:将 s s s中的一个字符’a’替换为字符串 t t t.求若干次(含零次)操作后能得到的不同字符串的数量.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入长度不超过 50 50 50的只包含字符’a’的字符串 s s s.第二行输入长度不超过 50 50 50的只包含小写英文字母的字符串 t t t.
对每组测试数据,若能得到无穷多种的不同字符串,输出 − 1 -1 −1;否则输出能得到的不同字符串的数量.
思路
①若 t t t为"a",则操作后不改变原串, a n s = 1 ans=1 ans=1.
②若 t t t的长度 ≥ 2 \geq 2 ≥2且包含字符’a’,则每次操作后 s s s中’a’的数量不减, a n s = I N F ans=INF ans=INF.
③若 t t t的长度 ≥ 2 \geq 2 ≥2且不包含字符’a’,则每次操作后 s s s中’a’的数量 − 1 -1 −1.
s s s中每个字符’a’都可选择替换或不替换, a n s = 2 l e n ans=2^{len} ans=2len,其中 l e n len len为 s s s的长度.
代码
void solve() {
string s, t; cin >> s >> t;
if (t == "a") cout << 1 << endl;
else {
bool ok = false;
for (auto ch : t) ok |= ch == 'a';
if (ok) cout << -1 << endl;
else cout << ((ll)1 << s.length()) << endl;
}
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
截至2022.09.14,无新题目