文章目录
A-骗红包
题意
在 [ 1 , 1000 ] [1,1000] [1,1000]中随机选择一个整数 n n n,zf和zn轮流操作,zf先手,每轮可以执行以下操作之一:
- n = ⌊ n 2 ⌋ n = \lfloor\frac{n}{2}\rfloor n=⌊2n⌋;
- n = n − 1 n = n - 1 n=n−1;
先把 n n n变为 0 0 0的玩家赢,获得 n n n个硬币。先进行 1000 1000 1000次游戏,求zf获得硬币数的期望。
思路
先考虑
n
n
n是定值时的情况。设
g
[
n
]
g[n]
g[n]表示选择的数字为
n
n
n,先手是否必胜,
g
[
n
]
=
1
g[n]=1
g[n]=1表示先手必胜,
g
[
n
]
=
0
g[n]=0
g[n]=0表示先手必败。显然当数字
⌊
n
2
⌋
\lfloor\frac{n}{2}\rfloor
⌊2n⌋或数字
n
−
1
n - 1
n−1先手必败时,数字
n
n
n先手必胜。所以有转移式
g
[
n
]
=
!
g
[
n
−
1
]
∣
∣
!
g
[
n
/
2
]
g[n]=!g[n-1]||!g[n/2]
g[n]=!g[n−1]∣∣!g[n/2]。
当
n
n
n为
[
1
,
1000
]
[1,1000]
[1,1000]中随机整数时,每次游戏每个数被选的概率为
1
1000
\frac{1}{1000}
10001,所以一次游戏的硬币数期望为
∑
i
=
1
1000
g
[
i
]
∗
i
∗
1
1000
\sum\limits_{i=1}^{1000}g[i]*i*\frac{1}{1000}
i=1∑1000g[i]∗i∗10001,
1000
1000
1000次游戏的硬币数期望为
∑
i
=
1
1000
g
[
i
]
∗
i
\sum\limits_{i=1}^{1000}g[i]*i
i=1∑1000g[i]∗i。
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
const int maxn = 1e3 + 5;
int g[maxn];
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
g[1] = 1;
fo(i, 2, 1000) g[i] = !g[i - 1] || !g[i / 2];
int ans = 0;
fo(i, 1, 1000) ans += g[i] * i;
printf("%d\n", ans);
return 0;
}
B-的面包工坊
题意
给定 n n n,需要寻找一个长度为 m m m(自己确定)的数列 { a i } \{a_i\} {ai},其中 n = ∑ i = 1 m a i n=\sum\limits_{i=1}^{m}a_i n=i=1∑mai,求 ∏ i = 1 m a i \prod\limits_{i=1}^{m}a_i i=1∏mai的最大值。
思路
设 f [ i ] f[i] f[i]为和为 i i i乘积的最大值,对于每个和 i i i,可以枚举最后一个数 j j j的大小,又由于和为 i − j i-j i−j的乘积最大值已知,所以有转移方程 f [ i ] = max { f [ i − j ] ∗ j } ( 1 ≤ j ≤ i ) f[i]=\max\{f[i-j]*j\}(1 \leq j \leq i) f[i]=max{f[i−j]∗j}(1≤j≤i)。
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
typedef long long ll;
const int maxn = 100 + 5;
int n;
ll f[maxn];
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
int T;
T = getint();
while (T--)
{
n = getint();
f[0] = 1;
fo(i, 1, n)
fo(j, 1, i)
f[i] = max(f[i], f[i - j] * j);
printf("%lld\n", f[n]);
}
return 0;
}
C-tmk一衣带水
题意
略
思路
略
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
printf("tmknb!");
return 0;
}
D-三角切
题意
给定三条边的长度,问是否能组成三角形,若能组成三角形,则输出三角形的类型(锐角,直角,钝角)。
思路
考虑能组成三角形的情况。假定三边分别为 a , b , c a,b,c a,b,c且 a < b < c a<b<c a<b<c。分情况考虑:
- 直角: c 2 = a 2 + b 2 c^2=a^2+b^2 c2=a2+b2;
- 钝角,考虑一种极限情况,当 a + b a+b a+b无限接近 c c c时,有 c = a + b c=a+b c=a+b,此时 c 2 > a 2 + b 2 c^2>a^2+b^2 c2>a2+b2。
- 锐角,结合1.2.可得 c 2 < a 2 + b 2 c^2<a^2+b^2 c2<a2+b2。
具体证明可以建系用向量点乘,自行思考。
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
typedef long long ll;
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
int T;
T = getint();
while (T--)
{
ll l[5];
l[1] = getint(); l[2] = getint(); l[3] = getint();
sort(l + 1, l + 1 + 3);
if (l[1] + l[2] <= l[3]) printf("No Solution\n");
else if (l[1] * l[1] + l[2] * l[2] == l[3] * l[3]) printf("Right\n");
else if (l[1] * l[1] + l[2] * l[2] < l[3] * l[3]) printf("Obtuse\n");
else printf("Acute\n");
}
return 0;
}
E-素数判定
题意
如题。
思路
自行查书,百度。
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
bool prime(int x)
{
if (x == 1) return false;
if (x == 2) return true;
for(int i = 2; i * i <= x; i++)
if (x % i == 0) return false;
return true;
}
int main()
{
int T;
T = getint();
while (T--)
{
printf(prime(getint())? "Yes\n" : "No\n");
}
return 0;
}
F-K阶Mex数列
题意
定义
m
e
x
(
l
,
r
)
mex(l,r)
mex(l,r)为
{
a
i
∣
l
≤
i
≤
r
}
\{a_i|l \leq i \leq r\}
{ai∣l≤i≤r}中最小不存在的非负整数,其中
{
a
i
}
\{a_i\}
{ai}为待求数列。给定
k
,
n
k,n
k,n,结合以下式子求
a
n
a_n
an。
a
n
=
{
n
n
<
k
m
e
x
(
n
−
k
,
n
−
1
)
n
≥
k
a_n=\begin{cases} n & n<k \\ mex(n-k,n-1) & n \geq k \end{cases}
an={nmex(n−k,n−1)n<kn≥k
思路
经模拟发现, { a n } \{a_n\} {an}为 0 , 1 , ⋯ , k − 1 , k , 0 , 1 , ⋯ , k − 1 , k 0,1,\cdots,k-1,k,0,1,\cdots,k-1,k 0,1,⋯,k−1,k,0,1,⋯,k−1,k不断循环,可得答案为 n % ( k + 1 ) n\%(k+1) n%(k+1)。
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
int T = getint();
while (T--)
{
int n, k;
n = getint(); k = getint();
printf("%d\n", n % (k + 1));
}
return 0;
}
G-秧歌Star不要上补习班
题意
给定二维坐标系中若干个点
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi),每个点有权值
v
i
v_i
vi。
接下来有次个询问,每次询问一个矩形/一条线段上的所有点的权值之和。
思路
解法一
用二维前缀和预处理权值之和,每次 O ( 1 ) O(1) O(1)查询,不懂二维前缀和自行百度。
解法二
对于每个询问,枚举所有点判断是否在给定矩形/线段上,若是则累加权值。
代码
解法一
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
const int maxn = 1e3 + 5;
int n, m, q, k;
int s[maxn][maxn];
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
int T;
T = 1;
while (T--)
{
n = getint(); m = getint(); k = getint();
fo(i, 1, n)
fo(j, 1, m) s[i][j] = 0;
fo(i, 1, k)
{
int x, y, v;
x = getint(); y = getint(); v = getint();
s[x][y] = v;
}
fo(i, 1, n)
fo(j, 1, m)
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + s[i][j];
q = getint();
fo(i, 1, q)
{
int x, y, xx, yy, u, d, l, r;
x = getint(); xx = getint(); y = getint(); yy = getint();
u = min(x, xx); d = max(x, xx);
l = min(y, yy); r = max(y, yy);
printf("%d\n", s[d][r] - s[d][l - 1] - s[u - 1][r] + s[u - 1][l - 1]);
}
}
return 0;
}
解法二
自己写
H 一道难题
题意
给定
n
,
m
n,m
n,m,表示以下数列:
1
,
2
,
.
.
.
,
m
−
1
,
m
,
1
,
2
,
⋯
1,2,...,m-1 ,m,1,2,\cdots
1,2,...,m−1,m,1,2,⋯(
1
1
1到
m
m
m循环)总共
n
n
n项。可以删除数列中的若干个数(包括不删和全删),求剩下的项组成的数列的种类数。当两个数列长度不同或有某位不同,则这两个数列为不同种类的数列。
思路
为了简洁起见下面称“剩下的项组成的数列的种类数”为“方案数”,“一个数列执行若干或不执行删除操作后得到的数列”为“剩余数列”。
考虑
d
p
dp
dp,设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为前
i
i
i个数,以
j
j
j结尾的剩余数列方案数。设数列第
i
i
i个数为
a
i
a_i
ai,则:当
j
≠
a
i
j \neq a_i
j=ai时,显然有
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]
f[i][j]=f[i−1][j],下面考虑
j
=
a
i
j=a_i
j=ai的情况。
按照前
i
−
1
i-1
i−1个数剩余数列结尾的
a
i
a_i
ai数量进行分类讨论:
- 前 i − 1 i-1 i−1个数的剩余数列为空,则方案数为 1 1 1。
- 前 i − 1 i-1 i−1个数的剩余数列结尾的 a i a_i ai数量为0,则剩余数列以 a i a_i ai以外的数结尾。
- 前 i − 1 i-1 i−1个数的剩余数列结尾的 a i a_i ai数量 ≥ 1 \geq 1 ≥1,则剩余数列以 a i a_i ai结尾。
综上,当
j
≠
a
i
j \neq a_i
j=ai时,
f
[
i
]
[
j
]
=
1
+
∑
k
=
1
m
f
[
i
−
1
]
[
k
]
f[i][j]=1+\sum\limits_{k=1}^m f[i-1][k]
f[i][j]=1+k=1∑mf[i−1][k],方便起见,可以设
f
[
i
]
[
0
]
f[i][0]
f[i][0]为空数列,令
f
[
i
]
[
0
]
=
1
f[i][0]=1
f[i][0]=1,则:
f
[
i
]
[
j
]
=
{
f
[
i
−
1
]
[
j
]
j
≠
a
i
∑
k
=
0
m
f
[
i
−
1
]
[
k
]
j
=
a
i
f[i][j]=\begin{cases} f[i-1][j] & j \neq a_i \\ \sum\limits_{k=0}^m f[i-1][k]& j=a_i \end{cases}
f[i][j]=⎩⎨⎧f[i−1][j]k=0∑mf[i−1][k]j=aij=ai
根据
f
[
i
]
[
j
]
f[i][j]
f[i][j]定义可得最终答案为
∑
i
=
0
m
f
[
n
]
[
i
]
\sum\limits_{i=0}^m f[n][i]
i=0∑mf[n][i]。
进一步观察式子可以发现,对于每个
i
i
i只有
f
[
i
]
[
a
i
]
f[i][a_i]
f[i][ai]发生改变,且
∑
k
=
0
m
f
[
i
−
1
]
[
k
]
\sum\limits_{k=0}^m f[i-1][k]
k=0∑mf[i−1][k]为更新前的
f
[
i
−
1
]
f[i-1]
f[i−1]数组的和,可以考虑用变量
s
u
m
sum
sum维护该和,每次更新
s
u
m
sum
sum即可,细节看代码。
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
typedef long long ll;
const int maxn = 1e6 + 5;
const ll mod = 1e9 + 7;
int n, m;
ll f[maxn];
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
int T = getint();
while (T--)
{
n = getint(); m = getint();
f[0] = f[1] = 1;
ll sum = 2;
fo(i, 2, m) f[i] = 0;
fo(i, 2, n)
{
int now = (i - 1) % m + 1;
ll nsum = (sum * 2 % mod - f[now] + mod) % mod;
f[now] = sum;
sum = nsum;
}
ll ans = 0;
fo(i, 0, m) (ans += f[i]) %= mod;
printf("%lld\n", ans);
}
return 0;
}
I 超消函数
题意
给定一个长度为 n n n的排列,每次可以选择两个数 a , b a,b a,b,删除这两个数,加入 g c d ( a , b ) gcd(a,b) gcd(a,b),并把 g c d ( a , b ) gcd(a,b) gcd(a,b)累加到 s u m sum sum中,要求最后只剩一个数。求 s u m sum sum的最大值。
思路
考虑
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b),设
a
=
2
p
1
∗
3
p
2
∗
⋯
,
b
=
2
p
1
′
∗
3
p
2
′
∗
⋯
a=2^{p_1}*3^{p_2}*\cdots,b=2^{p_1'}*3^{p_2'}*\cdots
a=2p1∗3p2∗⋯,b=2p1′∗3p2′∗⋯,
g
c
d
(
a
,
b
)
=
2
m
i
n
{
p
1
,
p
1
′
}
∗
3
m
i
n
{
p
2
,
p
2
′
}
∗
⋯
gcd(a,b)=2^{min\{p_1,p_1'\}}*3^{min\{p_2,p_2'\}}*\cdots
gcd(a,b)=2min{p1,p1′}∗3min{p2,p2′}∗⋯。
对于两个数
a
,
b
a,b
a,b,不妨令
a
>
b
a>b
a>b,有
g
c
d
(
a
,
b
)
≤
min
{
a
,
b
}
gcd(a,b) \leq \min\{a,b\}
gcd(a,b)≤min{a,b},当
b
b
b为
a
a
a的因子时取等号,即能取到最大值
b
b
b。所以,当
a
a
a固定时,
b
b
b取
a
a
a的最大因子时
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)有最大值,即
b
=
a
/
m
p
r
i
[
a
]
b=a/mpri[a]
b=a/mpri[a],其中
m
p
r
i
[
a
]
mpri[a]
mpri[a]表示
a
a
a的最小质因子。
考虑最大的数为
n
n
n,令另外一个数
m
=
n
/
m
p
r
i
[
n
]
m=n/mpri[n]
m=n/mpri[n]时,此时
g
c
d
(
n
,
m
)
gcd(n,m)
gcd(n,m)有最大值
m
m
m,删除
n
,
m
n,m
n,m后新加入的数为
m
m
m,相当于只删除了
n
n
n,现在问题变成长度为
n
−
1
n-1
n−1的子问题,重复以上操作即可。
若
m
m
m不取
n
/
m
p
r
i
[
n
]
n/mpri[n]
n/mpri[n]有没有可能更优?假设
m
≠
n
/
m
p
r
i
[
n
]
m \neq n/mpri[n]
m=n/mpri[n],有两种情况:
- m m m为 n n n的因子,此时 g c d ( n , m ) < g c d ( n , n / m p r i [ n ] ) gcd(n,m) < gcd(n,n/mpri[n]) gcd(n,m)<gcd(n,n/mpri[n]),且执行完删数、加数后是长度为 n − 1 n-1 n−1的子问题,不可能更优。
- m m m不为 n n n的因子,同样有 g c d ( n , m ) < g c d ( n , n / m p r i [ n ] ) gcd(n,m) < gcd(n,n/mpri[n]) gcd(n,m)<gcd(n,n/mpri[n]),但删除完 n , m n,m n,m后增加了一个小于 m m m的数,设其为 k k k,此时 k k k是 m m m的因子, k k k与其他数的 g c d gcd gcd值不会超过 m m m与其他数的 g c d gcd gcd值,即不可能更优。
所以答案为 ∑ i = 1 n i / m p r i [ i ] \sum\limits_{i=1}^ni/mpri[i] i=1∑ni/mpri[i],其中 m p r i [ i ] mpri[i] mpri[i]为 i i i的最小质因子。 m p r i [ i ] mpri[i] mpri[i]用任意质数筛求即可,下面代码用的是线性质数筛。
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
typedef long long ll;
const int maxn = 1e4 + 5;
int n, m;
int g[maxn], pri[maxn];
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
int T = getint();
while (T--)
{
n = getint();
fo(i, 1, n) g[i] = 0;
m = 0;
ll ans = 0;
fo(i, 2, n)
{
if (!g[i]) g[i] = i, pri[++m] = i;
fo(j, 1, m)
{
if (pri[j] > g[i] || i > n / pri[j]) break;
g[i * pri[j]] = pri[j];
}
ans += i / g[i];
}
printf("%lld\n", ans);
}
return 0;
}
J 最大公因数排序
题意
给定长度为 n n n的数列 { a i } \{a_i\} {ai},令 b i = 2 a i b_i=2^{a_i} bi=2ai,若 g c d ( b i , b j ) = m i n { b i } ( i ≠ j ) gcd(b_i,b_j)=min\{b_i\}(i \neq j) gcd(bi,bj)=min{bi}(i=j),则可以交换 b i b_i bi和 b j b_j bj,问能否将 { b i } \{b_i\} {bi}变为有序。
思路
首先
g
c
d
(
b
i
,
b
j
)
=
g
c
d
(
2
a
i
,
2
a
j
)
=
2
m
i
n
{
a
i
,
a
j
}
gcd(b_i,b_j)=gcd(2^{a_i},2^{a_j})=2^{min\{a_i,a_j\}}
gcd(bi,bj)=gcd(2ai,2aj)=2min{ai,aj},
g
c
d
(
b
i
,
b
j
)
=
m
i
n
{
b
i
}
(
i
≠
j
)
gcd(b_i,b_j)=min\{b_i\}(i \neq j)
gcd(bi,bj)=min{bi}(i=j)等价于
2
m
i
n
{
a
i
,
a
j
}
=
2
m
i
n
{
a
i
}
2^{min\{a_i,a_j\}}=2^{min\{a_i\}}
2min{ai,aj}=2min{ai},即
m
i
n
{
a
i
,
a
j
}
=
m
i
n
{
a
i
}
min\{a_i,a_j\}=min\{a_i\}
min{ai,aj}=min{ai}。
问题可以转化为对于
i
≠
j
i \neq j
i=j,
a
i
a_i
ai和
a
j
a_j
aj中存在
{
a
i
}
\{a_i\}
{ai}的最小值,则可以交换
a
i
,
a
j
a_i,a_j
ai,aj。问最后否将
{
a
i
}
\{a_i\}
{ai}变为有序。显然最小值是可以自由移动的,可以借助最小值将
{
a
i
}
\{a_i\}
{ai}变为有序,不清楚的可以手动模拟一下。所以答案恒为"Yes"。
代码
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)
const int maxn = 1e5 + 5;
int n;
int a[maxn];
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
int main()
{
int T;
T = getint();
while (T--)
{
n = getint();
fo(i, 1, n) a[i] = getint();
printf("Yes\n");
}
return 0;
}