2023 NOI 春测 T1 ~ T3 题解

2023 NOI 春测 T1 ~ T3 题解

T1 涂色游戏

P9117 [春季测试 2023] 涂色游戏

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 幂次

P9118 [春季测试 2023] 幂次

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 圣诞树

P9119 [春季测试 2023] 圣诞树

很显然的一个结论就是连接的路径一定不能相交。证明考虑相交的时候交换两个顶点的顺序一定更优。

假设已经连接的点的集合为 \(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);
}
posted @ 2023-03-08 14:55  Hanx16Msgr  阅读(338)  评论(0编辑  收藏  举报