专题一部分题解
@toc
B - Assistance Required
思路 :有点类似埃氏筛
, 使用数组标记元素是否需要出列
#include <iostream>
const int N = 4e4;
using namespace std;
int p[N],cnt;
int book[N];
int main() {
for (int i = 2;i < N;i ++) {
if(!book[i]) {
p [cnt ++ ] = i;
int k = 0;
for (int j = i + 1; j < N; j ++ ) {
if (!book[j]) {
k ++ ;
if (k == i) {
k = 0;
book[j] = 1;
}
}
}
}
}
int n;
while (cin >> n && n) {
cout << p[n - 1] << endl;
}
return 0;
}
E - Train Problem I
思路 :判断一个栈的出队序列是否合法 并 记录入队和出队的操作
#include <iostream>
#include <stack>
#include <cstring>
using namespace std;
char a[2000], b[2000];
int n;
int record[3000];//in 1 out 0
int main() {
while (cin >> n >> a >> b) {
stack<char> t;
int loc = 0, cnt = 0;
memset(record, 0, sizeof(record));
for (int i = 0; i < n; i ++ ) {
t.push(a[i]);
record[cnt ++ ] = 1;
while(!t.empty() && t.top() == b[loc]) {
t.pop();
loc ++ ;
record[cnt ++ ] = 0;
}
}
if (loc == n) {
cout << "Yes." << endl;
for (int i = 0; i < cnt; i ++ ) {
if (record[i]) cout << "in" << endl;
else cout << "out" << endl;
}
}
else {
cout << "No." << endl;
}
cout << "FINISH" << endl;
}
}
K - Boxes in a Line
思路: 双向链表 实现插入 交换 翻转
的操作
值得注意的是:
- 交换操作要特判相邻的情况
- 操作4如果将整个链表翻转, 时间复杂度不允许。 使用
fx
变量标记方向(0正 1反),fx = 1
时 操作1改为操作2,操作2改为操作1;此外,最后求奇数项和的时候,fx = 1
时,可以用总和减去顺序情况下的奇数项和,即为所求。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
#define ll long long
int l[N],r[N],fx;
ll ans;
void link(int x, int y) {
r[x] = y;
l[y] = x;
}
void init(int n) {
r[0] = 1;
l[n + 1] = n;
for (int i = 1; i <= n; i ++ ) {
l[i] = i - 1; r[i] = i + 1;
}
}
void op1(int x, int y) {
link(l[x],r[x]);link(l[y],x);link(x,y);
}
void op2(int x, int y) {
link(l[x],r[x]);link(x,r[y]);link(y,x);
}
void op3(int x, int y) {
int lx = l[x],rx = r[x],ly = l[y],ry = r[y];
if (y == r[x]) {
link(lx, y);link(y, x);link(x, ry);
} else if (x == r[y]) {
link(ly, x);link(x, y);link(y, rx);
} else {
link(lx, y);link(y, rx);link(ly, x);link(x, ry);
}
}
int main() {
int L, T;
int q = 1;
while (cin >> L >> T) {
ans = 0;
fx = 0;
init(L);
while (T -- ) {
int a, b, c;
cin >> a;
if (a == 4) fx = !fx;
else {
cin >> b >> c;
if (a == 1) {
if(!fx) op1(b, c);
else op2(b, c);
} else if (a == 2) {
if(!fx) op2(b, c);
else op1(b, c);
} else if (a == 3) {
op3(b, c);
}
}
}
int i = 0;
int cnt = 1;
for(i = r[i]; i != L + 1; i = r[i]) {
if(cnt & 1) ans += i;
cnt ++ ;
}
if (fx && L % 2 == 0) {
ans = (ll) L * (L + 1) / 2 - ans;
}
cout << "Case "<< q << ": " << ans << endl;
q ++ ;
}
}
L - Random Events
思路:
对于长度为
n
n
n的原序列与排序后序列有
k
k
k个不同的项,显然,当且仅当最后一个不同的项被排序好之后,原序列才合法。于是考虑每一个包含该项在内的操作。若都不能成功排序,无论之前是如何排序的,序列最终都不合法。记这种情况的概率为
p
p
p,则所求概率为
1
−
p
1-p
1−p.
#include <iostream>
#include <algorithm>
const int N = 1e6 + 10;
using namespace std;
int a[N], b[N], loc;
int main() {
int T;
cin >> T;
while (T -- ) {
double ans = 1;
int n, t;
cin >> n >> t;
for (int i = 1; i <= n; i ++ ) {
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + n + 1);
loc = n;
while (loc && a[loc] == b[loc]) {
-- loc;
}
while (t -- ) {
int k; double p;
cin >> k >> p;
if (k >= loc) ans *= (1 - p);
}
if (!loc) printf("1.000000\n");
else printf("%.6lf\n", 1 - ans);
}
}
M - Jumps
题意:起点为
0
0
0,第
i
i
i次有两种操作:向前
i
i
i步或后退
1
1
1步. 问, 最少多少次操作可以到达
X
X
X.
思路:
假设我们每一次操作都选择向前。那么 n n n次操作的最远距离显然是 1 + 2 + . . . + n = n ( n + 1 ) 2 1+2+...+n = \frac{n(n+1)}{2} 1+2+...+n=2n(n+1),考虑其中任意一次操作 k i ( 1 ≤ i ≤ n ) k_i(1\le i\le n) ki(1≤i≤n)改为后退一步。则所到达的位置变为 1 + 2 + . . . + n − i − 1 = n ( n + 1 ) 2 − i − 1 1+2+...+n-i-1 = \frac{n(n+1)}{2} - i -1 1+2+...+n−i−1=2n(n+1)−i−1。 简单来说,这意味着对于某个点 X X X,我们都可以先考虑一直向前的操作。当我们第一次超过 X X X时,即为最少操作数。(因为我们可以通过任意一步后退的做法,使位置向前 d d d个单位),值得注意的是 d d d的范围是 [ 2 , n + 1 ] [2,n + 1] [2,n+1]。这意味着,我们在 k k k次前进操作之后如果到达了 X + 1 X + 1 X+1。我们无法通过上述操作到达 X X X,只能再单独进行一次后退操作,所以最后操作数为 k + 1 。 k +1。 k+1。
#include <iostream>
using namespace std;
int main() {
int T;
cin >> T;
while (T -- ) {
int x; cin >> x;
int ans = 0;
while((ans + 1) * ans / 2 < x) ans ++ ;
if ((ans + 1) * ans / 2 == x + 1) ans ++ ;
cout << ans << endl;
}
}
O - Killjoy
思路:
1.如果分数都为
x
x
x,不需要操作
2.如果总分数的平均值为
x
x
x,只需操作一次
3.如果有至少一个人的分数为
x
x
x, 那么其他人可以使自己分数变为
x
x
x将自己感染再变回原分数。需要一次
4.如果无人分数为
x
x
x,则要先操作一次使得有一个
x
x
x,再进行第三步。需要两次
P - Saving the City
思路:贪心考虑每块空地填雷的价值
核心代码:
int flag = 0;
for (int i = 1; i <= len; i ++ ) {
if (s[i] == '1' ) flag = 1;
if (s[i] == '0') {
if (flag) cnt ++ ;
} else if (cnt) {
ans += (cnt * b < a) ? cnt * b : a;
cnt = 0;
}
}
cout << ( flag ? ans + a : ans ) << endl;
训练赛部分题解
A - Wizard of Orz
思路:
关键在于第三位是9而不是7,第一次wa,想成了987… 实际上选择在第二位停可以变成98901234567890123…
C - String Equality
思路:因为存在交换字母的操作,所以不需要考虑字母本身的位置,只需要考虑字母的出现次数,从a至z遍历,假设该字母在串一中出现
m
m
m次,在串二中出现
n
n
n次。有三种情况
1.
m
<
n
m< n
m<n, 操作2无法完全修改串一, 不行
2.
m
=
n
m = n
m=n,操作2可以修改串一
3.
m
>
n
m> n
m>n, 记
d
=
m
−
n
d = m - n
d=m−n,若
d
能
整
除
k
d能整除k
d能整除k,则可以通过操作2将该字母改成下一个字母, 即
让
m
i
+
1
+
d
让m_{i+1}+d
让mi+1+d