A. Prime Subtraction
题解:
所有素数都可以用2和3相加得到,所以这里只有在差值为1的时候不行
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
cin >> T;
while (T--) {
ll x, y;
cin >> x >> y;
ll res = x - y;
if (res == 1) cout << "NO" << endl;
else cout << "YES" << endl;
}
return 0;
}
B - Kill `Em All
题解:
因为爆炸后除了爆炸点外会把其他点往外挤,所以肯定是从最远的点开始爆破
所以排序一遍,然后去重(位于同一点一起炸了)计算即可
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5 + 10;
int T, N, r;
int a[MAX];
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> T;
while (T--) {
cin >> N >> r;
for (int i = 1; i <= N; i++) cin >> a[i];
sort(a + 1, a + 1 + N);
int tot = unique(a + 1, a + 1 + N) - a - 1, cnt = 0;
for (int i = tot; i >= 1; i--)
if (a[i] - cnt * r > 0) cnt++;//如果当前点在0右端,那就炸掉
cout << cnt << endl;
}
return 0;
}
C - Standard Free2play
题解:
首先,若当前位于平台
p
i
p_i
pi上,下一个平台
p
i
+
1
p_{i+1}
pi+1,则可以通过直接改变相邻两个平台的状态到达
p
i
+
1
+
1
p_{i+1}+1
pi+1+1的高度
所以对于某一平台
p
k
p_k
pk,我们一定可以到达
p
k
+
1
p_{k}+1
pk+1,下面有两种情况
①
p
k
+
1
=
p
k
−
1
p_{k+1}=p_{k}-1
pk+1=pk−1,显然,我们直接变换
p
i
p_i
pi和
p
k
+
1
p_k+1
pk+1的状态就可到达
p
k
−
1
p_{k}-1
pk−1平台,因为高度差为2,刚好不会摔死
②
p
k
+
1
<
p
k
−
1
p_{k+1}<p_{k}-1
pk+1<pk−1,也就是高度差大于2,那么如果中间没有平台,会摔死,所以这里我们需要买魔法项链将
p
k
−
1
p_{k}-1
pk−1的平台延伸出来,花费加一
不断重复这个过程即可
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 10;
int T, H, N;
int a[MAX];
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> T;
while (T--) {
cin >> H >> N;
for (int i = 1; i <= N; i++) cin >> a[i];
int cnt = 0;
a[N + 1] = 0;//在最后加一个0平台
for (int i = 2; i <= N; i++)
if (a[i] - a[i + 1] >= 2) cnt++;//如果不加0平台,如果最后的a[N]会有影响
else i++;
cout << cnt << endl;
}
return 0;
}
D - AB-string
题解:
可以发现,只有2种类型的子串不符合要求
①连续的一串
A
A
A+
B
B
B,如
A
A
A
A
A
B
AAAAAB
AAAAAB(AB可互换)
②
A
A
A+连续的一串
B
B
B,如
A
B
B
B
B
B
ABBBBB
ABBBBB
所以可以统计不符合要求的子串
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 3e5 + 10;
int N;
char s[MAX];
int main() {
scanf("%d%s", &N, s + 1);
ll ans = 1ll * N * (N - 1) / 2;//子串总数
int pre = 1;//上一个连续的字符所在位置
for (int i = 1; i <= N - 1; i++) {//正着扫一遍
if (s[i] != s[i + 1])//遇到两个不一样的
ans -= i + 1 - pre, pre = i + 1;
}
pre = N;
for (int i = N; i >= 2; i--) {//反着扫一遍
if (s[i] != s[i - 1])
ans -= pre - i, pre = i - 1;//这里ans本来是减掉pre - (i - 1)
//但是这样会重复减掉一遍AB,因为之前正着扫的时候已经减掉
}
cout << ans << endl;
return 0;
}
E - Keyboard Purchase(状压DP)
题解:
记
f
[
i
]
f[i]
f[i]为已经安装的键状态为
i
i
i的情况下,移动距离之和的最小值
记
v
[
i
]
[
j
]
v[i][j]
v[i][j]为 从键盘上的
i
i
i转移到
j
j
j的次数
如果当次安装 j j j键,并且键盘上已经有 k − 1 k-1 k−1个键已经安上去,那么未安装 m − k m-k m−k个键 与 已安装 k k k个键 之间的距离就多了1
不妨记, c n t cnt cnt为 未安装的键 与 已安装的键 经过增加一个键之后 增加的转移距离
那么就有了状态转移方程
f
[
i
]
=
min
j
=
1
m
(
f
[
i
]
,
f
[
i
⊕
(
1
<
<
j
)
]
+
c
n
t
)
f[i] = \displaystyle\min_{j=1}^m(f[i],f[i⊕(1<<j)]+cnt)
f[i]=j=1minm(f[i],f[i⊕(1<<j)]+cnt)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5 + 10;
int N, M;
char s[MAX];
int v[25][25], f[1 << 20];
int main() {
scanf("%d%d%s", &N, &M, s + 1);
for (int i = 1; i <= N - 1; i++) {
int x = s[i] - 'a', y = s[i + 1] - 'a';
v[x][y]++, v[y][x]++;
}
memset(f, 0x3f, sz(f));
f[0] = 0;
int mx = (1 << M) - 1;
for (int i = 0; i <= mx; i++) {
int cnt = 0;//计算当次贡献
for (int j = 0; j < M; j++)
for (int k = j + 1; k < M; k++)
if ((i >> j & 1) ^ (i >> k & 1))//已安装 和 未安装 之间的距离加1
cnt += v[j][k];
for (int j = 0; j < M; j++)
if (i >> j & 1)//属于该状态
f[i] = min(f[i], f[i ^ (1 << j)] + cnt);
}
cout << f[mx] << endl;
return 0;
}