2026.9.28 考试总结

NOIP 2021

估:100 + 100 + [48, 100] + 0

实:100 + 100 + 100 + 0

模拟退火太强啦!

t4 太难了不补。

number

P7960 [NOIP2021] 报数

考虑直接暴力预处理所有合法的数,每次询问二分即可。时间复杂度 \(O(n \ln n)\),但是常数极小。

code
#include <bits/stdc++.h>
using namespace std;
#define int int 
#define ll long long
#define AC return
#define AK return 0
#define el putchar('\n')
#define ys cout << "yes"
#define no cout << "no"
#define Ys cout << "Yes"
#define No cout << "No"
#define NO cout << "NO"
#define YS cout << "YES"
#define lowbit(i) (i & (-i))
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define Mod(x) (((x) % mod + mod) % mod)
#define void inline void
#define il inline
#define debug(x) cerr << #x << ' ' << x << '\n'
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
	int x, f = 1; char c;
	while(!isdigit(c = getchar())) if(c == '-') f = -f;
	x = c ^ 48;
	while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
	return x * f;
}
void ACehomoxue();
signed main() {
	srand(time(0));
	int t = 1;
	// freopen("number.in", "r", stdin);
	// freopen("number.out", "w", stdout);
	t = rd();
	while(t--) ACehomoxue();
}
const int mod = 998244353, maxn = 1e7 + 18;

int n;
vector <int> vec;
bool has[maxn];
il bool chk(int x) {
	while(x) {
		if(x % 10 == 7) return true;
		x /= 10;
	}
	return false;
}
void pre() {
	if(vec.size()) AC;
	for(int i = 1; i < maxn; i++) {
		if(has[i] || chk(i)) {
			has[i] = true;
			for(int j = 2 * i; j < maxn; j += i) has[j] = true; 
		} else vec.push_back(i);
	}
}

void ACehomoxue() {
	pre();
	n = rd();
	auto p = lower_bound(vec.begin(), vec.end(), n);
	if(*p != n) cout << -1;
	else {
		p++;
		cout << *p;
	}
	el;
}

sequence

P7961 [NOIP2021] 数列

考虑直接数位 dp。设 \(dp_{i, c, j, k}\) 表示二进制第 \(i\) 位及以上,以前填了 \(c\) 个数,小面进位了 \(j\) 次,还剩最多 \(k\) 个位置能填一。答案显然为 \(dp_{0, 0, 0, k}\)。对于初始状态,即当 \(i = m + 1\),若 \(k < \operatorname{popcount}(j)\) 或 \(c \ne n\) 显然为 \(0\),否则 \(n\) 个数字随便排有 \(c!\) 的方案数。考虑转移,枚举填了 \(t\) 个 \(v_i\),那么贡献应为 \({v_i} ^ t\),且内部没有顺序应该再乘上 \(\frac 1 {t!}\),然后直接向前面的 dp 数组转移,总结一下有:

\[dp_{i, c, j, k} = \sum_{t + c \le n} \frac {{v_i} ^ t} {t!} \times dp_{i + 1, c + t, \lfloor \frac {j + t} 2 \rfloor, k - ((j + t) \bmod 2)} \]

直接转移即可,时间复杂度 \(O(m n ^ 4)\)。做完了。

code
#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
#define AC return
#define AK return 0
#define el putchar('\n')
#define ys cout << "yes"
#define no cout << "no"
#define Ys cout << "Yes"
#define No cout << "No"
#define NO cout << "NO"
#define YS cout << "YES"
#define lowbit(i) (i & (-i))
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define Mod(x) (((x) % mod + mod) % mod)
#define void inline void
#define il inline
#define debug(x) cerr << #x << ' ' << x << '\n'
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
	int x, f = 1; char c;
	while(!isdigit(c = getchar())) if(c == '-') f = -f;
	x = c ^ 48;
	while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
	return x * f;
}
void ACehomoxue();
signed main() {
	srand(time(0));
	int t = 1;
	// freopen("sequence.in", "r", stdin);
	// freopen("sequence.out", "w", stdout);
//	t = rd();
	while(t--) ACehomoxue();
}
const int mod = 998244353, maxn = 33, maxm = 114;

int n, m, k, a[maxm];

int fact[maxm], inv[maxm];
il int ksm(int a, int k = mod - 2) { int res = 1; for(; k; k >>= 1, a = a * a % mod) if(k & 1) res = res * a % mod; return res; }
il int frac(int n, int m) { return n * fact[m - 1] % mod * inv[m] % mod; }
void pre() {
	fact[0] = 1;
	for(int i = 1; i < maxn; i++) fact[i] = fact[i - 1] * i % mod;
	inv[maxn - 1] = ksm(fact[maxn - 1]);
	for(int i = maxn - 2; i + 1; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
}

int dp[maxm][maxn][maxn][maxn];
bool vis[maxm][maxn][maxn][maxn];
il int f(int i, int c, int j, int k) {
	if(k < 0) AK;
	if(i > m) {
		if(c != n || k < 0) AK;
		int cnt = __builtin_popcount(j);
		if(cnt > k) AK;
		return fact[c];
	}
	if(vis[i][c][j][k]) return dp[i][c][j][k];
	vis[i][c][j][k] = true;
	int &F = dp[i][c][j][k];
	for(int v = 1, t = 0, tmp; c + t <= n; t++, v = v * a[i] % mod) {
		tmp = f(i + 1, c + t, (j + t) / 2, k - ((j + t) & 1));
		F = (F + tmp * inv[t] % mod * v) % mod;
	}
	return F;
}

void ACehomoxue() {
	pre();
	n = rd(), m = rd(), k = rd();
	for(int i = 0; i <= m; i++) a[i] = rd();
	cout << f(0, 0, 0, k);
	el;
}

variance

P7962 [NOIP2021] 方差

提供一种容错极高的模拟退火做法,和正解等价。

直接看发现很没思路,想一想转换成差分或前缀和。转换成差分之后,我们发现操作变成了交换两个差分的值,但注意第一个数不能换。由于方差最小,那么按照定义所有数字就应该离平均值最近,显然最后答案的差分序列是单谷的。直接二进制枚举每个数在谷的左边还是右边的 \(O(2 ^ n n)\) 做法可以拿到 \(n \le 20\) 的 \(48\) 分。对于 \(n > 20\),设 \(pos_i\) 表是 \(i\) 在谷的左边还是右边,不难发现我们确定 \(pos\) 之后就可以 \(O(n)\) 确定整个数组,那么我们可以对 \(pos\) 模拟退火,每次选一个数改变其和谷的位置关系。由于这个性质特别严卡时卡到 \(0.7s\) 就可以随便过了,测了 \(1000\) 次大样例只 wa 了 \(10\) 次不到。做完了。

code
#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
#define AC return
#define AK return 0
#define el putchar('\n')
#define ys cout << "yes"
#define no cout << "no"
#define Ys cout << "Yes"
#define No cout << "No"
#define NO cout << "NO"
#define YS cout << "YES"
#define lowbit(i) (i & (-i))
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define Mod(x) (((x) % mod + mod) % mod)
#define void inline void
#define il inline
#define debug(x) cerr << #x << ' ' << x << '\n'
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
	int x, f = 1; char c;
	while(!isdigit(c = getchar())) if(c == '-') f = -f;
	x = c ^ 48;
	while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
	return x * f;
}
void ACehomoxue();
signed main() {
	srand(time(0));
	int t = 1;
	// freopen("variance.in", "r", stdin);
	// freopen("variance.out", "w", stdout);
//	t = rd();
	while(t--) ACehomoxue();
}
const int mod = 998244353, maxn = 1e4 + 18;

int n, a[maxn], d[maxn], ans;
vector <int> v1, v2;

il int get() {
	int res = 0, sum = 0, x = 0;
	while(!v1.empty()) {
		x += v1.back();
		res += n * x * x;
		sum += x;
		v1.pop_back();
	}
	for(int d : v2) {
		x += d;
		res += n * x * x;
		sum += x;	
	}
	v1.clear(), v2.clear();
	return res - sum * sum;
}

bool pos[maxn];
il int calc() {
	v2.push_back(d[2]);
	for(int i = 3; i <= n; i++) {
		if(pos[i - 3] & 1) v1.push_back(d[i]);
		else v2.push_back(d[i]);
	}
	v1.push_back(d[1]);
	return get();
}

mt19937 rng(time(0));
const double eps = 1e-8;
il double rng01() { return (rng() % 10000000) / 1e7; }
void sa() {
	double t = 2e5, dt = 0.997;
	v2.push_back(d[2]);
	for(int i = 3; i <= n; i++) pos[i - 3] = (rng() & 1);
	int now = calc();
	while(t >= eps) {
		int p = rng() % (n - 2);
		pos[p] ^= 1;
		int nxt = calc();
		if(nxt < now || rng01() > exp(-t)) now = nxt;
		else pos[p] ^= 1;
		ans = min(ans, nxt);
		t *= dt;
	}
}

void ACehomoxue() {
	n = rd();
	for(int i = 1; i <= n; i++) a[i] = rd(), d[i] = a[i] - a[i - 1];
	sort(d + 2, d + n + 1);
	ans = 1e9;
	if(n <= 20) {
		for(int s = 0; s < (1 << (n - 2)); s++) {
			v2.push_back(d[2]);
			for(int i = 3; i <= n; i++) {
				if((s >> (i - 3)) & 1) v1.push_back(d[i]);
				else v2.push_back(d[i]);
			}
			v1.push_back(d[1]);
			ans = min(ans, get());
		}
		cout << ans;
		el;
	} else {
		while((double)clock() / CLOCKS_PER_SEC <= 0.7) sa();
		cout << ans;
		el;
	}
}
posted @ 2026-09-28 14:45  ACehomoxue  阅读(9)  评论(0)    收藏  举报