正睿 25 年联赛联合训练 Day 13

正睿 25 年联赛联合训练 Day 13

得分

T1 T2 T3 总分 排名
\(100\) \(70\) \(10\) \(180\) \(5/16\)

题解

T1 stone

首先答案 \(\le 8\) 是题目中给了的。这个性质比较奇特,我们设最后答案是 \(ans\),那么一定有 \(6\mid (n-ans)\)。原因在于 \(3\mid 3(k^2-k)\)\(2\mid (k^2-k)\)。那么也就是说 \(ans\equiv n\pmod 6\)。而又由于 \(ans\le 8\),所以我们有一些答案是可以直接得出的,只有 \(1,7\)\(2,8\) 两组需要判断一下。这个也是好判断的,这相当于判断一个数能否被一个或两个 \(k^2-k\) 表示出来。显然 \(k\) 只有 \(O(\sqrt n)\) 个,所以枚举一下即可,复杂度 \(O(T\sqrt n)\)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int Maxn = 2e5 + 5;
const int N = 130000;

int T;
ll n;

void solve() {
	cin >> n;
	int p = n % 3;
	ll n1 = (n - p) / 3, n2 = n1 - 1, n3 = n2 - 1;
	if(n2 % 2 == 0) {
		cout << 3 + p << '\n';
	}
	else {
		int flg = 0;
		if(p == 2) {
			for(int i = 1; i <= N; i++) {
				if(1ll * i * (i - 1) > n1) break;
				ll num = n1 - 1ll * i * (i - 1), t = sqrt(num);
				if(t * (t + 1) == num) {
					flg = 1; break;
				}
			}	
		}
		if(p != 0) {
			ll t = sqrt(n1);
			if(t * (t + 1) == n1) flg = 1;
		}
		if(flg) cout << p << '\n';
		else cout << 6 + p << '\n';
	}
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> T;
	while(T--) solve();
	return 0;
}

T2 palindrome

考虑先用 Manacher 跑出每个位置为中心的最长回文串,然后接下来我们枚举 \(AA^rA\) 的第一个分割点 \(i\),看这个分割点的 \(d_i\),那么第二个分割点 \(j\) 一定要满足 \(j\in [i+1,i+d_i]\)\(j-d_j\le i\)。显然这是一个二维数点,上一个扫描线求一下即可。复杂度 \(O(n\log n)\)

考场降智写了个巨大常数的主席树荣获 \(70\) pts……

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int Maxn = 2e6 + 5;
const int N = 2e6 + 1;
const int Inf = 2e9;

namespace cplx {bool beg;}

string s;
int n;

int d[Maxn << 1], a[Maxn];
struct node {
	int val, id;
}p[Maxn];
int tot = 0;
void Manacher() {
	string t = " #";
	for(int i = 1; i <= n; i++) t += s[i], t += '#';
	int m = t.size() - 1;
	int l = 1, r = 0;
	for(int i = 1; i <= m; i++) {
		int k = (i > r ? 1 : min(d[l + r - i], r - i + 1));
		while(t[i - k] == t[i + k]) k++;
		d[i] = k--;
		if(i + k > r) l = i - k, r = i + k;
	}
	for(int i = 1; i <= m; i++) {
		if(t[i] == '#') tot++, p[tot] = {tot - (d[i] >> 1), tot}, a[tot] = d[i] >> 1;
	}
	n = tot;
}

namespace BIT {
	int c[Maxn];
	int lowbit(int x) {return x & (-x);}
	void mdf(int x, int val) {for(int i = x; i <= n; i += lowbit(i)) c[i] += val; }
	int query(int x) {int sum = 0; for(int i = x; i; i -= lowbit(i)) sum += c[i]; return sum;}
}

namespace cplx {
	bool end;
	void mem() {cerr << (&beg - &end) / 1024.0 / 1024.0 << '\n';}
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> s;
	n = s.size(); s = ' ' + s;
	Manacher();
	sort(p + 1, p + n + 1, [](node x, node y){return x.val < y.val;});
	int pos = 1;
	ll ans = 0;
	for(int i = 1; i <= n; i++) {
		while(pos <= n && p[pos].val <= i) BIT::mdf(p[pos].id, 1), pos++;
		ans += BIT::query(i + a[i]) - BIT::query(i);
	}
	cout << ans << '\n';
	return 0;
}

T3 random

首先一个结论是一棵随机树的高度是 \(O(\sqrt n)\) 的,说明正解是一个 \(O(nh)\) 的做法。考虑用期望线性性拆贡献,只考虑每个点最后的期望。然后我们发现这只需要关注其根链上的节点。

现在我们要求出这条链上最后一个点的期望权值,继续拆贡献,然后就是求每个点对最后一个点贡献次数的期望值,用这个值乘上每个点对应的 \(v\) 即可得出最后一个点的期望值。考虑怎么求贡献次数的期望,假如我们要求第 \(k\) 个点给最后一个第 \(m\) 个点的期望贡献次数,实际上这和求第一个点给第 \(m-k+1\) 的期望贡献次数是一致的。把这个值记作 \(p_{m-k+1}\)

现在我们只用考虑怎样求 \(p_i\) 了。这个值的含义是第一个点给第 \(i\) 个点的期望贡献次数。继续拆一次贡献,发现第一个点给第 \(i\) 个点每贡献 \(1\) 就代表其走过了排列中的一个上升子序列,原因显然。所以我们可以求出所有排列中上升子序列的个数,这就是贡献的总数。这个也是好求的,枚举子序列长度 \(d\),方案数为:

\[\sum \binom{i-1}{d-1}\binom{i}{d}(i-d)! \]

求出这个值再除以 \(m!\) 就是 \(p_i\)。然后就可以直接求答案了。复杂度 \(O(n\sqrt n)\)

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int Maxn = 2e5 + 5;
const int Inf = 2e9;
const int Mod = 1e9 + 7;

int n, m;
int head[Maxn], edgenum, w[Maxn];
struct node {
	int nxt, to;
}edge[Maxn];

void add(int u, int v) {
	edge[++edgenum] = {head[u], v}; head[u] = edgenum;
	edge[++edgenum] = {head[v], u}; head[v] = edgenum;
}

int qpow(int a, int b) {
	int res = 1;
	while(b) {
		if(b & 1) res = res * a % Mod;
		a = a * a % Mod; b >>= 1;
	}
	return res;
}

int fac[Maxn], inv[Maxn];
void init() {
	fac[0] = 1; for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % Mod;
	inv[n] = qpow(fac[n], Mod - 2);
	for(int i = n; i >= 1; i--) inv[i - 1] = inv[i] * i % Mod;
}

int C(int n, int m) {
	if(n < 0 || m < 0 || n < m) return 0;
	return fac[n] * inv[m] % Mod * inv[n - m] % Mod;
}

int f[Maxn];
int dep[Maxn], st[Maxn], top;

void dfs1(int x, int fth) {
	dep[x] = dep[fth] + 1; m = max(m, dep[x]);
	for(int i = head[x]; i; i = edge[i].nxt) {
		int to = edge[i].to;
		if(to == fth) continue;
		dfs1(to, x);
	}
}

int ans = 0;
void dfs2(int x, int fth) {
	st[++top] = x;
	for(int i = 1; i <= top; i++) {
		(ans += w[st[i]] * f[top - i + 1] % Mod) %= Mod;
	}
	(ans += w[x]) %= Mod;
	for(int i = head[x]; i; i = edge[i].nxt) {
		int to = edge[i].to;
		if(to == fth) continue;
		dfs2(to, x);
	}
	top--;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	init();
	for(int i = 1; i < n; i++) {
		int u, v; cin >> u >> v;
		add(u, v);
	}
	for(int i = 1; i <= n; i++) cin >> w[i];
	dfs1(1, 0);
	for(int i = 1; i <= m; i++) {
		for(int j = 1; j <= i; j++) {
			f[i] = (f[i] + C(i - 1, j - 1) * C(i, j) % Mod * fac[i - j] % Mod) % Mod;
		}
		f[i] = f[i] * inv[i] % Mod;
	}
	dfs2(1, 0);
	cout << ans * fac[n] % Mod; 
	return 0;
}
posted @ 2025-03-21 16:54  UKE_Automation  阅读(24)  评论(0)    收藏  举报