[Codeforces] combinatorics (R1600) Part.1
题单:https://codeforces.com/problemset?tags=combinatorics,1201-1600
52B. Right Triangles
原题指路:https://codeforces.com/problemset/problem/52/B
题意 (
2
s
2\ \mathrm{s}
2 s)
给定一个
n
×
m
(
1
≤
n
,
m
≤
1000
)
n\times m\ \ (1\leq n,m\leq 1000)
n×m (1≤n,m≤1000)的只包含字符’.‘和’*‘的字符矩阵,求其中三个顶点为’*'且直角边平行于坐标轴的直角三角形的个数.
思路
每个’*‘只能与所在行和列的其他’*‘构成直角三角形.统计第
i
i
i行、第
j
j
j列的’*‘的个数
r
o
w
i
row_i
rowi和
c
o
l
j
col_j
colj,则在
(
i
,
j
)
(i,j)
(i,j)处的’*'对答案的贡献为
(
r
o
w
i
−
1
)
(
c
o
l
i
−
1
)
(row_i-1)(col_i-1)
(rowi−1)(coli−1),其中
−
1
-1
−1即排除自己.
注意答案可能爆int.
代码
void solve() {
int n, m; cin >> n >> m;
vector<vector<char>> a(n, vector<char>(m));
vi row(n), col(m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
if (a[i][j] == '*') row[i]++, col[j]++;
}
}
ll ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++)
if (a[i][j] == '*') ans += (row[i] - 1) * (col[j] - 1);
}
cout << ans;
}
int main() {
solve();
}
107B. Basketball Team
原题指路:https://codeforces.com/problemset/problem/107/B
题意
小明是包含
n
n
n个队员的队伍中的一员.队员来自不同专业.有编号
1
∼
m
1\sim m
1∼m的
m
m
m个专业,其中人数分别为
s
1
,
⋯
,
s
n
s_1,\cdots,s_n
s1,⋯,sn,小明来自专业
h
(
1
≤
h
≤
m
)
h\ \ (1\leq h\leq m)
h (1≤h≤m).求小明至少有一个同专业的队友的概率,误差不超过
1
e
−
6
1\mathrm{e}-6
1e−6.若组成队伍的人数不足,输出
−
1
-1
−1.
第一行输入三个整数
n
,
m
,
h
(
1
≤
n
≤
100
,
1
≤
m
≤
1000
,
1
≤
h
≤
m
)
n,m,h\ \ (1\leq n\leq 100,1\leq m\leq 1000,1\leq h\leq m)
n,m,h (1≤n≤100,1≤m≤1000,1≤h≤m).第二行输入
m
m
m个整数
s
1
,
⋯
,
s
n
(
1
≤
s
i
≤
100
)
s_1,\cdots,s_n\ \ (1\leq s_i\leq 100)
s1,⋯,sn (1≤si≤100),其中
s
h
s_h
sh包含小明.
思路
1
1
1减不含队友的概率即可.
代码
void solve() {
int n, m, h; cin >> n >> m >> h;
vi s(m);
for (int i = 0; i < m; i++) cin >> s[i];
n--, s[--h]--; // 除掉小明
int sum = 0;
for (int i = 0; i < m; i++) sum += s[i];
if (sum < n) {
cout << -1;
return;
}
double ans = 1;
for (int i = 0; i < n; i++) ans *= 1 - (double)s[h] / (sum--);
cout << fixed << setprecision(12) << 1 - ans << endl;
}
int main() {
solve();
}
124B. Permutations
原题指路:https://codeforces.com/problemset/problem/124/B
题意
给定
n
n
n个
k
(
1
≤
n
,
k
≤
8
)
k\ \ (1\leq n,k\leq 8)
k (1≤n,k≤8)位的整数,调整其中某些数的数码顺序,使得这些数中的最大值与最小值之差最小,初始的数和调整后的数都可以出现前导零.
思路
注意到
n
,
k
n,k
n,k很小,而
8
!
=
40320
8!=40320
8!=40320,枚举所有情况即可.
注意本题不能贪心.
代码
void solve() {
int n, k; cin >> n >> k;
vector<vi> a(n, vi(k));
for (int i = 0; i < n; i++) {
for (int j = 0; j < k; j++) {
char ch; cin >> ch;
a[i][j] = ch - '0';
}
}
vi p(k); // 排列
for (int i = 0; i < k; i++) p[i] = i;
int ans = INF;
do {
int minnum = INF, maxnum = -INF;
for (int i = 0; i < n; i++) {
int cur = 0;
for (int j = 0; j < k; j++) cur = cur * 10 + a[i][p[j]];
minnum = min(minnum, cur), maxnum = max(maxnum, cur);
}
ans = min(ans, maxnum - minnum);
} while (next_permutation(all(p)));
cout << ans;
}
int main() {
solve();
}
131C. The World is a Theatre
原题指路:https://codeforces.com/problemset/problem/131/C
题意
(
2
s
)
(2\ \mathrm{s})
(2 s)
从
n
(
4
≤
n
≤
30
)
n\ \ (4\leq n\leq 30)
n (4≤n≤30)个男生和
m
(
1
≤
m
≤
30
)
m\ \ (1\leq m\leq 30)
m (1≤m≤30)个女生选出
t
(
5
≤
t
≤
n
+
m
)
t\ \ (5\leq t\leq n+m)
t (5≤t≤n+m)人组队,要求男生不少于
4
4
4人,女生不少于
1
1
1人,求方案数.
思路
设选
b
o
y
boy
boy个男生,则
a
n
s
=
∑
b
o
y
C
n
b
o
y
C
m
t
−
b
o
y
\displaystyle ans=\sum_{boy} C_n^{boy}C_m^{t-boy}
ans=boy∑CnboyCmt−boy.
考察
b
o
y
boy
boy的取值范围,剩下的
(
t
−
b
o
y
)
(t-boy)
(t−boy)人选女生.
①下限:男生至少选
4
4
4人,若女生全选后剩下的人数超过
4
4
4人,则男生需选
>
4
>4
>4人,故
b
o
y
≥
max
{
4
,
t
−
m
}
boy\geq \max\{4,t-m\}
boy≥max{4,t−m}.
②上限:男生至多选
(
t
−
1
)
(t-1)
(t−1)人,故
b
o
y
≤
min
{
n
,
t
−
1
}
boy\leq \min\{n,t-1\}
boy≤min{n,t−1}.
枚举所有的
b
o
y
boy
boy,累加答案即可.
代码
ll C(int n, int m) { // 组合数C(n,m)
ll res = 1;
for (int i = n - m + 1; i <= n; i++) res = res * i / (i - n + m);
return res;
}
void solve() {
int n, m, t; cin >> n >> m >> t;
ll ans = 0;
for (int boy = max(4, t - m); boy <= min(n, t - 1); boy++) // 枚举男生人数
ans += C(n, boy) * C(m, t - boy);
cout << ans;
}
int main() {
solve();
}
150B. Quantity of Strings
原题指路:https://codeforces.com/problemset/problem/150/B
题意
(
2
s
)
(2\ \mathrm{s})
(2 s)
给定三个整数
n
,
m
,
k
(
1
≤
n
,
m
,
k
≤
2000
)
n,m,k\ \ (1\leq n,m,k\leq 2000)
n,m,k (1≤n,m,k≤2000),求有多少个由
m
m
m种字符构成的长度为
n
n
n的字符串,使得其任意长度为
k
k
k的子串都是回文串,答案对
1
e
9
+
7
1\mathrm{e}9+7
1e9+7取模.
思路I
(1)
k
=
1
k=1
k=1或
k
>
n
k>n
k>n时,
a
n
s
=
m
n
ans=m^n
ans=mn.
(2)
k
=
n
k=n
k=n时,整个字符串为回文串.
①
n
n
n为偶数时,只需选定前
n
2
\dfrac{n}{2}
2n个字符,
a
n
s
=
m
n
2
ans=m^{\frac{n}{2}}
ans=m2n.
②
n
n
n为偶数时,除选定前
n
2
\dfrac{n}{2}
2n个字符外还需选定中间的字符,
a
n
s
=
m
n
2
+
1
ans=m^{\frac{n}{2}+1}
ans=m2n+1.
综上,
a
n
s
=
m
⌊
n
2
⌋
ans=m^{\left\lfloor\frac{n}{2}\right\rfloor}
ans=m⌊2n⌋.
(3)
k
k
k为偶数时,所有字符都相同,
a
n
s
=
m
ans=m
ans=m.
(4)
k
k
k为奇数时,字符串形如
a
b
a
b
a
b
⋯
ababab\cdots
ababab⋯,
a
n
s
=
m
2
ans=m^2
ans=m2.
代码I
const int MOD = 1e9 + 7;
void solve() {
int n, m, k; cin >> n >> m >> k;
if (k == 1 || k > n) cout << qpow(m, n, MOD);
else if (k == n) cout << qpow(m, (n + 1) / 2, MOD);
else if (k & 1) cout << m * m;
else cout << m;
}
int main() {
solve();
}
思路II
设字符串
s
s
s下标从
0
0
0开始.枚举
s
s
s的长度为
k
k
k的子串的起点
i
i
i,则对
∀
j
∈
[
0
,
k
−
1
]
\forall j\in[0,k-1]
∀j∈[0,k−1],有
s
[
i
+
j
]
=
s
[
i
+
k
−
j
−
1
]
s[i+j]=s[i+k-j-1]
s[i+j]=s[i+k−j−1].以下标
0
∼
(
n
−
1
)
0\sim (n-1)
0∼(n−1)为节点,在需要相同字符的下标间连边建图,则
a
n
s
=
m
c
n
t
ans=m^{cnt}
ans=mcnt,其中
c
n
t
cnt
cnt为图中的连通块的个数.
代码II
const int MAXN = 2005;
const int MOD = 1e9 + 7;
vi edges[MAXN];
bool vis[MAXN];
void dfs(int u) {
vis[u] = true;
for (auto v : edges[u])
if (!vis[v]) dfs(v);
}
void solve() {
int n, m, k; cin >> n >> m >> k;
for (int i = 0; i < n - k + 1; i++) { // 枚举长度为k的子串的起点
for (int j = 0; j < k; j++)
edges[i + j].push_back(i + k - j - 1); // 需要相同字符的下标间连边
}
int cnt = 0; // 连通块个数
for (int i = 0; i < n; i++) {
if (!vis[i]) {
cnt++;
dfs(i);
}
}
cout << qpow(m, cnt, MOD);
}
int main() {
solve();
}
思路III
同思路II,但用并查集来维护连通块.
代码III
const int MAXN = 2005;
const int MOD = 1e9 + 7;
int fa[MAXN];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
void merge(int x, int y) {
x = find(x), y = find(y);
if (x != y) fa[x] = y;
}
void solve() {
int n, m, k; cin >> n >> m >> k;
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= n - k + 1; i++) { // 枚举长度为k的子串的起点
for (int j = 0; j < k; j++) // 注意即使i从1开始枚举,j也从0开始枚举
merge(i + j, i + k - j - 1); // 合并需要相同字符的下标
}
int cnt = 0; // 连通块个数
for (int i = 1; i <= n; i++) cnt += fa[i] == i;
cout << qpow(m, cnt, MOD);
}
int main() {
solve();
}
152C. Pocket Book
原题指路:https://codeforces.com/problemset/problem/152/C
题意
(
2
s
)
(2\ \mathrm{s})
(2 s)
有编号
1
∼
n
1\sim n
1∼n的
n
n
n个长度为
m
m
m的字符串.现有操作:选择三个整数
i
,
j
,
k
(
1
≤
i
<
j
≤
n
,
1
≤
k
≤
m
)
i,j,k\ \ (1\leq i<j\leq n,1\leq k\leq m)
i,j,k (1≤i<j≤n,1≤k≤m),交换字符串
i
i
i与
j
j
j的长度为
k
k
k的前缀.问若干次操作后能得到多少种不同的名字,答案对
1
e
9
+
7
1\mathrm{e}9+7
1e9+7取模.
第一行输入两个整数
n
,
m
(
1
≤
n
,
m
≤
100
)
n,m\ \ (1\leq n,m\leq 100)
n,m (1≤n,m≤100).接下来
n
n
n行每行输入一个长度为
m
m
m的且只包含小写英文字母的字符串.
思路
设所有字符串在下标
i
(
0
≤
i
≤
m
−
1
)
i\ \ (0\leq i\leq m-1)
i (0≤i≤m−1)处有
c
n
t
i
cnt_i
cnti种字符,则
a
n
s
=
∏
i
=
0
m
−
1
c
n
t
i
\displaystyle ans=\prod_{i=0}^{m-1} cnt_i
ans=i=0∏m−1cnti.
代码
const int MAXN = 105;
const int MOD = 1e9 + 7;
set<char> s[MAXN]; // 存每一列的字符
void solve() {
int n, m; cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
char ch; cin >> ch;
s[j].insert(ch);
}
}
int ans = 1;
for (int i = 0; i < m; i++) ans = (ll)ans * s[i].size() % MOD;
cout << ans;
}
int main() {
solve();
}
171B. Star
原题指路:https://codeforces.com/problemset/problem/171/B
题意
(
2
s
)
(2\ \mathrm{s})
(2 s)
![在这里插入图片描述](https://img-blog.csdnimg.cn/166900c947e3437684d4361b83c05d41.png#pic_center)
如上图,给定一个整数
n
(
1
≤
n
≤
18257
)
n\ \ (1\leq n\leq 18257)
n (1≤n≤18257),求第
n
n
n个Star Number.
思路
Star Number的通项
a
n
=
6
n
(
n
−
1
)
+
1
a_n=6n(n-1)+1
an=6n(n−1)+1.
代码
void solve() {
int n; cin >> n;
cout << 6 * n * (n - 1) + 1;
}
int main() {
solve();
}
204A. Little Elephant and Interval
原题指路:https://codeforces.com/problemset/problem/204/A
题意
(
2
s
)
(2\ \mathrm{s})
(2 s)
给定两个整数
l
,
r
(
1
≤
l
≤
r
≤
1
e
18
)
l,r\ \ (1\leq l\leq r\leq 1\mathrm{e}18)
l,r (1≤l≤r≤1e18),问有多少个
x
∈
[
l
,
r
]
s
.
t
.
x
x\in[l,r]\ s.t.\ x
x∈[l,r] s.t. x的十进制表示中最高位与最低位相同.
思路
f
(
n
)
f(n)
f(n)表示
[
1
,
n
]
[1,n]
[1,n]中十进制表示的最高位与最低位相同的数的个数,则
a
n
s
=
f
(
r
)
−
f
(
l
−
1
)
ans=f(r)-f(l-1)
ans=f(r)−f(l−1).
下面讨论如何求
f
(
n
)
f(n)
f(n).
①显然
[
1
,
9
]
[1,9]
[1,9]的整数都满足条件.
②对
n
≥
10
n\geq 10
n≥10的情况,注意到个位数每
10
10
10个一循环,则
f
(
n
)
≤
⌊
n
10
⌋
+
9
f(n)\leq \left\lfloor\dfrac{n}{10}\right\rfloor+9
f(n)≤⌊10n⌋+9.
n
=
21
n=21
n=21时,上式求得的结果为
11
11
11,但事实上只有
1
,
⋯
,
9
,
11
1,\cdots,9,11
1,⋯,9,11满足条件,
这是因为当
n
n
n的首位
>
>
>末尾时末位无法取到与首位相同,故答案需
−
1
-1
−1.
代码
ll cal(ll n) {
if (n <= 9) return n;
else return n / 10 + 9 - (n / qpow(10, to_string(n).length() - 1) > n % 10);
}
void solve() {
ll l, r; cin >> l >> r;
cout << cal(r) - cal(l - 1);
}
int main() {
solve();
}
251A. Points on Line
原题指路:https://codeforces.com/problemset/problem/251/A
题意
(
2
s
)
(2\ \mathrm{s})
(2 s)
x
x
x轴上有
n
n
n个点
x
1
,
⋯
,
x
n
x_1,\cdots,x_n
x1,⋯,xn.给定一个整数
d
d
d,选三个相异的点,使得它们两两的距离不超过
d
d
d,求方案数.
第一行输入两个整数
n
,
d
(
1
≤
n
≤
1
e
5
,
1
≤
d
≤
1
e
9
)
n,d\ \ (1\leq n\leq 1\mathrm{e}5,1\leq d\leq 1\mathrm{e}9)
n,d (1≤n≤1e5,1≤d≤1e9).第二行严格升序地输入
n
n
n个整数
x
1
,
⋯
,
x
n
(
∣
x
i
∣
≤
1
e
9
)
x_1,\cdots,x_n\ \ (|x_i|\leq 1\mathrm{e}9)
x1,⋯,xn (∣xi∣≤1e9).
思路
枚举每个起点
l
l
l,用双指针找到最靠右的使得
x
[
r
]
−
x
[
l
]
≤
d
x[r]-x[l]\leq d
x[r]−x[l]≤d的点
r
r
r,则该起点对答案的贡献为在中间的
(
r
−
l
)
(r-l)
(r−l)个点中任选两个,即
C
r
−
l
2
C_{r-l}^2
Cr−l2.
代码
ll C(int n) { // 组合数C(n,2)
return (ll)n * (n - 1) / 2;
}
void solve() {
int n, d; cin >> n >> d;
vi x(n);
for (auto& xi : x) cin >> xi;
ll ans = 0;
for (int l = 0, r = 0; l < n; l++) { // 枚举起点
while (r + 1 < n && x[r + 1] - x[l] <= d) r++; // 找到最靠右的使得x[r]-x[l]≤d的点
ans += C(r - l);
}
cout << ans;
}
int main() {
solve();
}
272D. Dima and Two Sequences
原题指路:https://codeforces.com/problemset/problem/272/D
题意
(
2
s
)
(2\ \mathrm{s})
(2 s)
有两个包含
n
n
n个点的点列
(
a
1
,
1
)
,
⋯
,
(
a
n
,
n
)
(a_1,1),\cdots,(a_n,n)
(a1,1),⋯,(an,n)和
(
b
1
,
1
)
,
⋯
,
(
b
n
,
n
)
(b_1,1),\cdots,(b_n,n)
(b1,1),⋯,(bn,n).将这些点组成一个长度为
2
n
2n
2n的点列使得
x
x
x坐标不降,求方案数,答案对
m
m
m取模.两方案不同当且仅当至少存在一个位置的点不同.
第一行输入一个整数
n
(
1
≤
n
≤
1
e
5
)
n\ \ (1\leq n\leq 1\mathrm{e}5)
n (1≤n≤1e5).第二行输入
n
n
n个整数
a
1
,
⋯
,
a
n
(
1
≤
a
i
≤
1
e
9
)
a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}9)
a1,⋯,an (1≤ai≤1e9).第三行输入
n
n
n个整数
b
1
,
⋯
,
b
n
(
1
≤
b
i
≤
1
e
9
)
b_1,\cdots,b_n\ \ (1\leq b_i\leq 1\mathrm{e}9)
b1,⋯,bn (1≤bi≤1e9).第四行输入一个整数
m
(
2
≤
m
≤
1
e
9
+
7
)
m\ \ (2\leq m\leq 1\mathrm{e}9+7)
m (2≤m≤1e9+7).
思路
s
a
m
e
x
i
samex_i
samexi表示
x
=
i
x=i
x=i的点的个数,
s
a
m
e
y
i
samey_i
sameyi表示
x
=
i
x=i
x=i的点中有多少对
y
y
y相等的点,
s
u
m
=
∑
i
f
i
\displaystyle sum=\sum_i f_i
sum=i∑fi,则
a
n
s
=
∏
i
s
a
m
e
x
i
!
2
s
u
m
ans=\dfrac{\displaystyle \prod_i samex_i!}{2^{sum}}
ans=2sumi∏samexi!,其中除以
2
s
u
m
2^{sum}
2sum是因为
x
x
x相等的点中至多有
2
2
2个
y
y
y相等的点,这两点的是无序的.
注意
2
2
2在模
m
m
m下的逆元未必存在,需在求阶乘的过程中约掉
2
2
2.
代码
const int MAXN = 1e5 + 5;
int MOD;
int n;
int a[MAXN], b[MAXN];
umap<int, int> samex; // samex[i]表示x=i的点的个数
umap<int, int> samey; // samey[i]表示x=i的点中有多少对y相等的点
uset<int> s; // 已计算贡献的点
int cal(int x, int y) { // x! / 2^y
int res = 1;
for (int i = 1; i <= x; i++) {
if (y && i % 2 == 0) res = (ll)res * i / 2 % MOD, y--;
else res = (ll)res * i % MOD;
}
return res;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
cin >> MOD;
for (int i = 1; i <= n; i++) {
samex[a[i]]++, samex[b[i]]++;
if (a[i] == b[i]) samey[a[i]]++;
}
int ans = 1;
for (int i = 1; i <= n; i++) {
if (!s.count(a[i])) {
ans = (ll)ans * cal(samex[a[i]], samey[a[i]]) % MOD;
s.insert(a[i]);
}
if (!s.count(b[i])) {
ans = (ll)ans * cal(samex[b[i]], samey[b[i]]) % MOD;
s.insert(b[i]);
}
}
cout << ans;
}
int main() {
solve();
}