题目按难度从简到难手动排序————题记
T1 :最大公约数(gcd.c/cpp/pas)
【 题目描述】:
给定
n
n
n 个数, 从中选出
K
K
K 个。
A
l
i
c
e
Alice
Alice 想让
K
K
K 个数的最大公约数尽可能大, 求最大的最大公约数。
【输入数据】:
第一行两个正整数
n
,
K
n, K
n,K。
第二行
n
n
n 个正整数, 即给定的
n
n
n 个数。
【输出数据】:
输出一个正整数表示最大的最大公约数。
【样例输入】
3 1
1 2 3
【样例输出】:
3
【数据范围】:
对于
30
30
30%的数据,
n
<
=
20
n <= 20
n<=20。
对于
60
60
60%的数据, 保证输入中所有数小于
5000
5000
5000。
对于
100
100
100%的数据, 保证输入中所有数小于
5
∗
1
0
5
5*10^{5}
5∗105,
K
<
=
n
K <= n
K<=n。
心路历程:一拿到这道题,一眼看出30分的方法是用 d f s dfs dfs枚举选数方案,突然想到前几天做的一道题(见算法竞赛进阶指南Interval GCD),里面有个操作是询问 [ l , r ] [l,r] [l,r]区间中的最大公约数,上面用的方法是求差分序列,我感觉可以借鉴,于是开始想差分序列,以失败告终,突然想到二分答案,貌似可行,复杂度是 O ( n l o g n ) O(n log n) O(nlogn),于是开始打代码,一遍过了大样例,心里感觉很稳,但是突然意识到这道题的答案并可以二分,举个例栗子(现在二分到 3 3 3,发现 3 3 3不是符合题意的 g c d gcd gcd值,就会向 < = 3 <=3 <=3的区间搜索,但是可能真正的 g c d gcd gcd值是一个 > 3 >3 >3的值),舍不得白写,就加了随机化,当 n < = 20 n<=20 n<=20时跑 d f s dfs dfs,当 n > = 20 n>=20 n>=20时跑二分答案,然后看下一道了。
正解:开桶存下每个权值出现了几次,然后枚举 g c d gcd gcd值。 枚举它的所有倍数, 看出现次数是否大于等于 k k k 就可以了。 复杂度 O ( n + 2 n + 3 n + … + n n ) = O ( n l n n ) ≈ O ( n l o g n ) O(n+\frac {2} {n}+\frac {3} {n}+…+\frac {n} {n})=O(nlnn)≈O(nlogn) O(n+n2+n3+…+nn)=O(nlnn)≈O(nlogn),证明看“调和级数求和”。
正解代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 600001;
int n, m, k;
int b[N], a[N];
int Max = -1, maxx = -1;
int vis[N] = {};
int main() {
int lx = 0;
freopen("gcd.in","r",stdin);
freopen("gcd.out","w",stdout);
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%d",&a[i]), maxx = max(maxx, a[i]), b[a[i]]++;
for (int i=maxx;i>=1;i--) {
int ans = 0;
for (int j=i;j<=maxx;j=j+i)
ans += b[j];
if (ans >= k) {
printf("%d",i);
return 0;
}
}
return 0;
}
T2.计数(count.c/cpp/pas)
【题目描述】:
A
l
i
c
e
Alice
Alice 和
B
o
b
Bob
Bob 在平面直角坐标系中下棋。
A
l
i
c
e
Alice
Alice 的棋子初始时在
(
0
,
0
)
(0, 0)
(0,0)位置, 要走到
(
a
,
b
)
(a, b)
(a,b)位
置;
B
o
b
Bob
Bob的棋子初始时在
(
c
,
0
)
(c, 0)
(c,0)位置, 要走到
(
a
,
d
)
(a, d)
(a,d)位置。棋子只能沿
x
x
x轴或
y
y
y 轴正方向移动若
干个单位长度, 问有多少种移动方案使两颗棋子的移动路径不相交。
【输入数据】
输入一行 4 个正整数, 依次为
a
,
b
,
c
,
d
a, b, c, d
a,b,c,d。
【输出数据】:
输出总方案数对质数 100000007 取模的结果。
【样例输入】:
3 2 1 1
【样例输出】:
6
【样例解释】:
A 走
(
0
,
0
)
→
(
0
,
2
)
→
(
3
,
2
(0,0) → (0,2) → (3,2
(0,0)→(0,2)→(3,2) 时, B 有 3 种走法:
(
1
,
0
)
→
(
1
,
1
)
→
(
3
,
1
)
(1,0) → (1,1) → (3,1)
(1,0)→(1,1)→(3,1)
(
1
,
0
)
→
(
2
,
0
)
→
(
2
,
1
)
→
(
3
,
1
)
(1,0) → (2,0) → (2,1) → (3,1)
(1,0)→(2,0)→(2,1)→(3,1)
(
1
,
0
)
→
(
3
,
0
)
→
(
3
,
1
)
(1,0) → (3,0) → (3,1)
(1,0)→(3,0)→(3,1)
A 走
(
0
,
0
)
→
(
0
,
1
)
→
(
1
,
1
)
→
(
1
,
2
)
→
(
3
,
2
(0,0) → (0,1) → (1,1) → (1,2) → (3,2
(0,0)→(0,1)→(1,1)→(1,2)→(3,2)时, B 有 2 种走法。
A 走
(
0
,
0
)
→
(
0
,
1
)
→
(
2
,
1
)
→
(
2
,
2
)
→
(
3
,
2
)
(0,0) → (0,1) → (2,1) → (2,2) → (3,2)
(0,0)→(0,1)→(2,1)→(2,2)→(3,2)时, B 有 1 种走法。
【数据范围】:
对于 50%的数据,
a
+
b
<
=
20
a + b <= 20
a+b<=20。
对于 70%的数据,
a
+
b
<
=
2
∗
1
0
4
a + b <= 2*10^4
a+b<=2∗104。
对于 100%的数据,
a
+
b
<
=
2
∗
1
0
5
a + b <= 2*10^5
a+b<=2∗105 且
a
>
c
,
b
>
d
a > c, b > d
a>c,b>d。
心路历程: 拿到这道题,第一反应就是求方案数,但是没有好的思路去求这个方案数,于是想了10min后就将这道题放在一边,看下一题了。
正解:
如果不考虑路径相交的问题,那么总共有
C
a
−
c
+
d
a
−
c
∗
C
a
+
b
a
C_{a-c+d}^{a-c} * C_{a+b}^{a}
Ca−c+da−c∗Ca+ba种方法。
现在问题是, 这些方案中,有多少是路径相交的呢?
注意到如果两条路径相交,可以看成
A
,
B
A,B
A,B 棋子交换目的地。
考虑从
(
0
,
0
)
(0,0)
(0,0)走到
(
a
,
d
)
(a,d)
(a,d),那么一共有
C
a
+
d
a
C_{a+d}^{a}
Ca+da种走法;从
(
c
,
0
)
(c,0)
(c,0)走到
(
a
,
b
)
(a,b)
(a,b)一共有
C
a
−
c
+
d
a
−
c
C_{a-c+d}^{a-c}
Ca−c+da−c种走法。
相交的路径总共是
C
a
+
d
a
∗
C
a
−
c
+
d
a
−
c
C_{a+d}^{a} * C_{a-c+d}^{a-c}
Ca+da∗Ca−c+da−c种。
两式作差即为原题所求。
良心提醒:组合数的求解可以使用逆元,求逆元及逆元的使用方法请自行 https://www.baidu.com.
本题最大坑点:题目模数为 1 0 8 + 7 10^{8}+7 108+7,而不是 1 0 9 + 7 10^{9}+7 109+7
正解代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1110000;
const int Mod = 100000007;
int n, m;
long long c[N] = {};
long long a, b, cc, d;
inline long long Power(long long x,long long y) {
long long ans = 1;
while (y){
if (y & 1) ans = (ans * x) % Mod;
y >>= 1;
x = x * x % Mod;
}
return ans % Mod;
}
inline long long C(int x,int y) {
if (x < y) swap(x, y);
return c[x] * Power(c[x-y]*c[y]%Mod, Mod-2) % Mod;
}
int main() {
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%lld%lld%lld%lld",&a,&b,&cc,&d);
long long sum = 1;
c[1] = 1;
for (int i=2;i<=500001;i++)
c[i] = (c[i-1] * i) % Mod;
sum = sum * C(a+b, a) * C(a-cc+d, a-cc) % Mod;
sum = (sum - (C(a+d, a) * C(a-cc+b, a-cc)) % Mod + Mod) % Mod;
printf("%lld",sum);
return 0;
}
T3.异色弧(arc)
【题目描述】:
在数轴上有
n
n
n个点,它们的坐标分别为
(
1
,
0
)
,
(
2
,
0
)
,
…
,
(
n
,
0
)
(1,0), (2,0), …, (n,0)
(1,0),(2,0),…,(n,0)。每个点都有一个颜色,坐标为
(
i
,
0
)
(i,0)
(i,0)的点颜色为
a
i
a_i
ai。
A
l
i
c
e
Alice
Alice 在所有颜色相同的点对间都画上了圆弧。更具体地, 如果有
a
i
a_i
ai =
a
j
a_j
aj且
i
i
i ≠
j
j
j, Alice
会画一条圆弧连接
(
i
,
0
)
(i,0)
(i,0)与
(
j
,
0
)
(j,0)
(j,0), 且这条弧颜色为
a
i
a_i
ai,所有圆弧都在第一象限内。
A
l
i
c
e
Alice
Alice 想知道有多少对不同颜色的圆弧相交了。
【输入数据】:
第一行一个整数 n 表示点数。
第二行 n 个整数 ai 表示所有点的颜色。
【输出数据】:
一行一个整数表示答案,答案对质数 1 0 9 + 7 10^{9}+7 109+7 取模。
【样例输入】:
8 1
2 3 1 2 3 2 1
【样例输出】:
8
【数据范围】:
对于 20%的数据:
1
<
=
n
<
=
10
1 <= n <= 10
1<=n<=10。
对于 40%的数据:
1
<
=
n
<
=
1000
1 <= n <= 1000
1<=n<=1000。
另有 30%的数据, 每种颜色的点数不超过
20
20
20。
对于 100%的数据:
1
<
=
n
,
a
i
<
=
1
e
5
1 <= n, ai <= 1e5
1<=n,ai<=1e5。
心路历程:题目很好懂,求相交数,20分的代码也很好写,直接暴力 d f s dfs dfs就行了,开始考虑40分做法,Nmin后发现,对于一个点对 i , j i,j i,j,将区间 [ i , j ] [i,j] [i,j]的贡献和区间 [ j + 1 , n ] [j+1,n] [j+1,n]的贡献相乘,就是 i , j i,j i,j的贡献,时间不多了,就没有深入考虑,直接敲代码,一遍过样例后就不管它了。果然,最后评测结果爆0了。
正解:
考虑容斥,计算所有异色圆弧对数减去
A
A
B
B
AABB
AABB 式与
A
B
B
A
ABBA
ABBA 式
A
A
B
B
AABB
AABB式容易计算,枚举第二个A,左右两边的贡献可以线性得出
A
B
B
A
ABBA
ABBA式的情况比较复杂,考虑平衡规划的思想
根据颜色的出现次数
c
n
t
i
cnt_i
cnti分类,假设以k为界
分
c
n
t
A
cnt_A
cntA>=
k
k
k,
c
n
t
A
cnt_A
cntA <
k
k
k且
c
n
t
B
cnt_B
cntB >=
k
k
k,
c
n
t
A
cnt_A
cntA <
k
k
k且
c
n
t
B
cnt_B
cntB <
k
k
k三种情况进行讨论
记prei_j表示前
i
i
i个点颜色
j
j
j的出现次数
P
a
r
t
1
Part1
Part1:
c
n
t
A
cnt_A
cntA >=
k
k
k
若
B
B
B的弧为
(
x
,
y
)
(x, y)
(x,y)则贡献为prex_A *
(
c
n
t
A
−
p
r
e
y
A
)
(cnt_A - prey_A)
(cntA−preyA)
由于
A
A
A不超过
k
n
\frac {k} {n}
nk种,因此我们枚举
A
A
A再枚举
B
B
B,在扫描颜色B的点的同时维护前缀和计算贡献,复杂度为
O
(
n
2
k
)
O( \frac{n^2}{k} )
O(kn2)
P
a
r
t
3
Part3
Part3
c
n
t
A
cnt_A
cntA <
k
k
k且
c
n
t
B
cnt_B
cntB >=
k
k
k
与上面类似,但此时我们只能枚举颜色
B
B
B,列出计算式子后,仍然枚举颜色为
A
A
A的点,并计算和式的变化
P
a
r
t
3
Part3
Part3
c
n
t
A
cnt_A
cntA <
k
k
k 且
c
n
t
B
cnt_B
cntB <
k
k
k
类似一个二维数点,数据结构维护即可.