『ZOI Round #1』官方题解
T1 - 奇妙数
70 分解法
若直接枚举 L 与 R 之间的奇妙数,将因子 2,3,5,7 从当前枚举的数中除去。若结果为 1,说明该数只有这 4 个质因子,是一个奇妙数。否则不是。
例如 60 是一个奇妙数。将因子 2 从中去除,得 60÷2÷2=15。再将因子 3 从中去除,得 15÷3=5。再将因子 55 从中去除,得 5÷5=1。最后将因子 7 从中去除,由于 60 中不含因子 7,忽略此操作。结果为 1,故这是一个奇妙数。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
ll l, r, ans = 0; cin >> l >> r;
for (ll i = l; i <= r; i++) {
ll t = i;
while (t % 2 == 0) t /= 2;
while (t % 3 == 0) t /= 3;
while (t % 5 == 0) t /= 5;
while (t % 7 == 0) t /= 7;
if (t == 1) ans++;
}
cout << ans;
return 0;
}
100 分解法
考虑到 a,b,c,d 会很小,故可以直接使用四重循环枚举 a,b,c,da,b,c,d,然后算出 2a×3b×5c×7d,看它是否在 L 与 R 之间即可。
需要注意,a,b,c,d 可以是零。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll pp(ll a, ll b) {
ll res = 1;
for (ll i = 1; i <= b; i++) {
res *= a;
}
return res;
}
int main() {
ll l, r, ans = 0;
cin >> l >> r;
for (ll i2 = 0; i2 <= 35; i2++) {
for (ll i3 = 0; i3 <= 20; i3++) {
for (ll i5 = 0; i5 <= 14; i5++) {
for (ll i7 = 0; i7 <= 12; i7++) {
ll now = pp(2, i2) * pp(3, i3) * pp(5, i5) * pp(7, i7);
if (l <= now && now <= r) {
ans++;
}
}
}
}
}
cout << ans;
return 0;
}
T2 - 等地铁
40 分解法
对于每一次操作,我们算出地铁从首发站发出后需要多长时间到第 x 站,即 A1+A2+⋯+Ax−1。对发车间隔时间进行一系列取模操作即可算出答案。
需要特判的是,t 分钟时,首班地铁还没有到第 x 站,这是答案即为首班车到这里的时间(即 A1+A2+⋯+Ax−1)减去 t。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll a[114514];
ll pre(ll x) {
ll res = 0;
for (ll i = 1; i <= x; i++) {
res += a[i];
}
return res;
}
int main() {
ll n, k, q;
cin >> n >> k >> q;
for (ll i = 2; i <= n; i++) {
cin >> a[i];
}
while (q--) {
ll x, t;
cin >> x >> t;
ll ans = t - pre(x);
if (ans >= 0) {
ans %= k;
ans = k - ans;
ans %= k;
} else {
ans = pre(x) - t;
}
cout << ans <<endl;
}
return 0;
}
100 分解法
可以发现,每次询问计算的 A1+A2+⋯+Ax−1 是数列前缀和。这里考虑前缀和优化,可以避免多次计算同样的东西。
对于每次询问的求和操作,我们可以预处理出一个前缀和。这样每次询问的时间复杂度就降为 O(1) 了。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll pre[114514];
int main() {
ll n, k, q;
cin >> n >> k >> q;
for (ll i = 2; i <= n; i++) {
cin >> pre[i];
pre[i] += pre[i - 1];
}
while (q--) {
ll x, t;
cin >> x >> t;
ll ans = t - pre[x];
if (ans >= 0) {
ans %= k;
ans = k - ans;
ans %= k;
} else {
ans = pre[x] - t;
}
cout << ans <<endl;
}
return 0;
}
T3 - 俄罗斯方块
10 分解法
注意到子任务 3 中,N=1。
记录 A 部分和 B 部分的左右端点,分类讨论进行计算即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
char c[55];
int main() {
ll n, m;
cin >> n >> m;
for (ll i = 1; i <= m; i++) {
cin >> c[i];
}
ll la = 0, lb = 0, ra = 0, rb = 0;
for (ll i = 1; i <= m; i++) {
if (c[i] == 'A') {
ra = i;
if (la == 0) {
la = i;
}
}
if (c[i] == 'B') {
rb = i;
if (lb == 0) {
lb = i;
}
}
}
if (ra <= lb) {
bool ok = 1;
for (ll i = ra; i <= lb; i++) {
if (c[i] == '#') {
ok = 0;
break;
}
}
if (ok) {
cout << "Yes" << endl;
cout << lb - la << endl;
} else {
cout << "No" << endl;
cout << -1 << endl;
}
} else {
bool ok = 1;
for (ll i = rb; i <= la; i++) {
if (c[i] == '#') {
ok = 0;
break;
}
}
if (ok) {
cout << "Yes" << endl;
cout << la - lb << endl;
} else {
cout << "No" << endl;
cout << -1 << endl;
}
}
return 0;
}
100 分解法
由于数据范围很小,考虑暴力的 BFS,需要一定的代码能力。
BFS 的队列中存储当前方块的每一个方格的位置(使用 vector 存储),以及当前移动步数。对于转移,我们枚举四个移动方向,并算出移动后各方格的位置,判断是否合法(即该位置是否在游戏区域中且不是墙)。对于每一个 BFS 的状态,与 B 部分进行比较,若发现相同,直接输出当前移动步数,结束程序。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, m;
ll dx[] = {0, 1, 0, -1};
ll dy[] = {1, 0, -1, 0};
char a[60][60];
struct pos{
ll x, y;
bool operator < (const pos &qw) const {
if (x != qw.x) {
return x < qw.x;
}
return y < qw.y;
}
};
struct node{
vector<pos> v;
ll t;
};
bool in(ll xx, ll yy) {
if (xx < 1 || xx > n) {
return 0;
}
if (yy < 1 || yy > m) {
return 0;
}
return 1;
}
vector<pos> v1, v2;
queue<node> q;
bool vis[60][60];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (ll i = 1; i <= n; i++) {
for (ll j = 1; j <= m; j++) {
cin >> a[i][j];
if (a[i][j] == 'A') {
v1.push_back({i, j});
}
if (a[i][j] == 'B') {
v2.push_back({i, j});
}
}
}
q.push({v1, 0});
vis[v1[0].x][v1[0].y] = 1;
while (!q.empty()) {
node h = q.front();
q.pop();
bool ok = 1;
for (ll i = 0; i < (ll)v2.size(); i++) {
if (h.v[i].x != v2[i].x || h.v[i].y != v2[i].y) {
ok = 0;
break;
}
}
if (ok) {
cout << "Yes" << endl;
cout << h.t << endl;
return 0;
}
for (ll i = 0; i < 4; i++) {
vector<pos> v3;
bool ok2 = 1;
for (pos u : h.v) {
if (!in(u.x + dx[i], u.y + dy[i]) || a[u.x + dx[i]][u.y + dy[i]] == '#') {
ok2 = 0;
break;
}
v3.push_back({u.x + dx[i], u.y + dy[i]});
}
if (!ok2 || vis[v3[0].x][v3[0].y]) {
continue;
}
vis[v3[0].x][v3[0].y] = 1;
q.push({v3, h.t + 1});
}
}
cout << "No" << endl;
cout << -1 << endl;
return 0;
}
T4 - 数轴
此题不展示部分分解法。
100 分解法
首先,可以不用考虑 N:如果将整个数轴向左平移 N 个单位长度,xi 变成 xi−N,答案不会发生任何变化。
现在考虑如何计算每次询问。假设 ti 秒全部向右走,那么最终小 Z 会来到点 ti。如果 ti<xi,方案数肯定为 0。否则需要将他的终点向左调整 ti−xi 个单位长度。将一次向右走换成一次向左走,可以将终点向左调整 2 个单位长度,于是需要选择 2ti−xi 个向右走换成向左走(如果 2∤ti−xi 或 ti−xi2>ti2ti−xi>ti,方案数为 0)。所以方案数为 (2ti−xiti)。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll p = 1e9 + 7ll;
ll fac[1919810];
ll quickpow(ll n, ll m) {
if (m == 1ll) {
return n % p;
}
if (m == 0ll) {
return 1ll % p;
}
if (m % 2ll) {
ll ret = (quickpow(n, m / 2ll)) % p;
return ret * ret % p * n % p;
} else {
ll ret = (quickpow(n, m / 2ll)) % p;
return ret * ret % p;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
fac[0] = 1;
for (ll i = 1; i <= 1000005; i++) {
fac[i] = i * fac[i - 1] % p;
}
ll n, tc;
cin >> n >> tc;
while (tc--) {
ll x, y;
cin >> x >> y;
if ((y - n + x) % 2) {
cout << 0 << endl;
continue;
}
y = (y - n + x) / 2;
if (y < 0 || x - y < 0) {
cout << 0 << endl;
continue;
}
ll ans = fac[x] % p * quickpow(fac[x - y] % p * fac[y] % p, p - 2ll) % p;
cout << ans << endl;
}
return 0;
}
总结
本次比赛对标 CSP-J,难度较为简单。
祝各位 OIer 们在信息的道路上越走越远!
完结撒花!
1122



被折叠的 条评论
为什么被折叠?



