2023 NOI 春测 T1 ~ T3 题解
2023 NOI 春测 T1 ~ T3 题解
T1 涂色游戏
Solution
考虑 \(L_i\) 表示第 \(i\) 行最后一次被染成什么颜色,\(U_i\) 表示第 \(i\) 列最后一次被染成什么颜色,同时记录下这些操作的时间先后顺序。
那么对于最终的格子 \((i,j)\),它的颜色就是 \(L_i\) 和 \(U_j\) 中更晚的一个,简单判断一下然后输出即可。
Code
#include<bits/stdc++.h>
using namespace std;
constexpr int _N = 1e5 + 5;
int n, m, q;
pair<int, int> L[_N], U[_N];
void Init() {
for (int i = 1; i <= n; ++i) {
L[i] = make_pair(0, 0);
}
for (int i = 1; i <= m; ++i) {
U[i] = make_pair(0, 0);
}
}
void Solve() {
cin >> n >> m >> q;
Init();
for (int i = 1, opt, x, y; i <= q; ++i) {
cin >> opt >> x >> y;
if (opt == 0) {
L[x] = make_pair(y, i);
} else {
U[x] = make_pair(y, i);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (L[i].second > U[j].second) {
cout << L[i].first << ' ';
} else {
cout << U[j].first << ' ';
}
}
cout << '\n';
}
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) Solve();
}
T2 幂次
Solution
我就搬个原题的题解过来吧:CF955C Sad powers
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
using i64 = long long;
i64 n, a[100005];
int k;
inline long double Qpow(int x, int y) {
long double res = 1, base = x;
for (; y; y >>= 1, base *= base) {
if (y & 1) {
res *= base;
}
}
return res;
}
inline int GetRoot(int x, int y) {
int l = 1, r = x;
while (l <= r) {
int mid = (l + r) >> 1;
if (Qpow(mid, y) > x) r = mid - 1;
else l = mid + 1;
}
return r;
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> k;
i64 res = 1;
int lim = 0;
for (int i = k; ; ++i) {
int v = GetRoot(n, i);
lim = i;
if (v <= 1) break;
a[i] = v - 1;
}
for (int i = lim - 1; i >= k; --i) {
for (int j = i + i; j <= lim; j += i) {
a[i] -= a[j];
}
}
for (int i = k; i < lim; ++i) {
res += a[i];
}
cout << res << '\n';
}
T3 圣诞树
很显然的一个结论就是连接的路径一定不能相交。证明考虑相交的时候交换两个顶点的顺序一定更优。
假设已经连接的点的集合为 \(S\),那么 \(S\) 一定在凸多边形上是连续的,因此有一个很显然的 DP:设 \(f(i,j,0/1)\) 表示从 \(k\) 逆时针转选了 \(i\) 个,顺时针转选了 \(j\) 个,当前在左侧 / 右侧。转移很明显(用 \(L(x)\) 表示 \(k\) 逆时针开始的第 \(x\) 个,\(R(x)\) 表示顺时针的第 \(x\) 个):
\[\begin{array}{rll}
f(i,j,0)&\gets& f(i-1,j,0)+\text{dis}(L(i-1),L(i))\\
f(i,j,0)&\gets& f(i-1,j,1)+\text{dis}(R(j),L(i))\\
f(i,j,1)&\gets& f(i,j-1,0)+\text{dis}(L(i),R(j))\\
f(i,j,1)&\gets& f(i,j-1,1)+\text{dis}(R(j-1),R(j))
\end{array}
\]
最终答案就是 \(\min\limits_{0\le i<n}\min{\Large(}f(i,n-i-1,0),f(i,n-i-1,1){\Large)}\)。
题目要求输出方案,那么就在转移的过程中再记录一个 \(\text{from}(i,j,0/1)\) 表示当前状态是又哪一个转移过来的,最后遍历输出即可。
Code
#include<bits/stdc++.h>
using namespace std;
using Ldouble = long double;
constexpr int _N = 1e3 + 5;
int n, K;
struct Node {
Ldouble x, y;
} po[_N];
struct TriPair {
int fir, sec, thi;
TriPair() {}
TriPair(int fir, int sec, int thi) : fir(fir), sec(sec), thi(thi) {}
};
Ldouble f[_N][_N][2];
TriPair from[_N][_N][2];
inline Ldouble Sqr(Ldouble x)
{return x * x;}
inline Ldouble Dist(int i, int j)
{return sqrt(Sqr(po[i].x - po[j].x) + Sqr(po[i].y - po[j].y));}
inline int Lp(int x)
{return K - x <= 0 ? K - x + n : K - x;}
inline int Rp(int x)
{return K + x > n ? K + x - n : K + x;}
void Output(TriPair cur) {
if (cur.fir == 0 && cur.sec == 0)
return cout << K << ' ', void();
Output(from[cur.fir][cur.sec][cur.thi]);
if (cur.thi) cout << Rp(cur.sec) << ' ';
else cout << Lp(cur.fir) << ' ';
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> po[i].x >> po[i].y;
double tmpMax = -1e8;
for (int i = 1; i <= n; ++i)
if (po[i].y > tmpMax)
tmpMax = po[i].y, K = i;
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= n; ++j)
f[i][j][0] = f[i][j][1] = 1e18;
f[0][0][0] = f[0][0][1] = 0;
Ldouble val;
for (int i = 0; i < n; ++i) {
for (int j = 0, jend = n - j; j < jend; ++j) {
if (i) {
val = f[i - 1][j][0] + Dist(Lp(i - 1), Lp(i));
if (f[i][j][0] > val)
f[i][j][0] = val, from[i][j][0] = TriPair(i - 1, j, 0);
val = f[i - 1][j][1] + Dist(Rp(j), Lp(i));
if (f[i][j][0] > val)
f[i][j][0] = val, from[i][j][0] = TriPair(i - 1, j, 1);
}
if (j) {
val = f[i][j - 1][0] + Dist(Lp(i), Rp(j));
if (f[i][j][1] > val)
f[i][j][1] = val, from[i][j][1] = TriPair(i, j - 1, 0);
val = f[i][j - 1][1] + Dist(Rp(j - 1), Rp(j));
if (f[i][j][1] > val)
f[i][j][1] = val, from[i][j][1] = TriPair(i, j - 1, 1);
}
}
}
Ldouble ans = 1e18;
TriPair pos;
for (int i = 0; i < n; ++i) {
if (ans > f[i][n - i - 1][0])
ans = f[i][n - i - 1][0], pos = TriPair(i, n - i - 1, 0);
if (ans > f[i][n - i - 1][1])
ans = f[i][n - i - 1][1], pos = TriPair(i, n - i - 1, 1);
}
Output(pos);
}