正睿 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\),方案数为:
求出这个值再除以 \(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;
}

浙公网安备 33010602011771号