Niareh 的四月 题解
A - 回忆
题意
在正整数集合中每次选取一对
分析
考虑到最终的
因此,最终集合的模就是集合中最大的元素除以
复杂度:
STD
#include <iostream>
using namespace std;
int gcd (int a, int b) { return b ? gcd (b, a % b) : a; }
int main (void)
{
int n, res, v;
ios::sync_with_stdio (false), cin.tie (0), cout.tie (0);
cin >> n >> res, --n;
while (n--) cin >> v, res = gcd (res, v);
cout << v / res << '\n';
return 0;
}
B - 街道
题意
给定一棵树,找其中距离小于等于
分析
换根 DP 模板题。我们设
然后,我们设
不妨设点
复杂度:
STD
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
const int N = 2e5 + 10, K = 25;
ll f[N][K], g[N][K];
int k;
vector<int> e[N];
void dfs1 (int u, int fa)
{
g[u][0] = 1;
for (auto v : e[u])
if (v != fa) {
dfs1 (v, u);
for (int i = 1; i <= k; i++) g[u][i] += g[v][i - 1];
}
}
void dfs2 (int u, int fa)
{
for (auto v : e[u])
if (v != fa) {
f[v][0] = 1, f[v][1] = g[v][1] + 1;
for (int i = k; i >= 2; i--) f[v][i] = f[u][i - 1] + g[v][i] - g[v][i - 2];
}
for (auto v : e[u]) if (v != fa) dfs2 (v, u);
}
int main (void)
{
int n;
ll res = 0;
ios::sync_with_stdio (false), cin.tie (0), cout.tie (0);
cin >> n >> k;
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
e[u].emplace_back (v), e[v].emplace_back (u);
}
dfs1 (1, 0);
for (int i = 0; i <= k; i++) f[1][i] = g[1][i];
dfs2 (1, 0);
for (int i = 1; i <= n; i++) for (int j = 0; j <= k; j++) res += f[i][j];
cout << res << '\n';
return 0;
}
C - 数环
题意
从
分析
注意数据范围
将序列均分成两半。对于左边,我们搜出所有和并存在数组中,并排序。对于右边,我们枚举能加出的每个和。考虑右边每个和能和左边哪些组合产生最大值。
设在右边搜出了
复杂度:
STD
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 42;
int z[N], n, m, res = 0;
vector<int> w;
void dfs1 (int x, int sum)
{
if (x > n / 2) return w.push_back (sum), void ();
dfs1 (x + 1, sum), dfs1 (x + 1, (sum + z[x]) % m);
}
void dfs2 (int x, int sum)
{
if (x > n) {
int p = lower_bound (begin (w), end (w), m - sum)[-1];
res = max (res, max (p + sum, (w.back () + sum) % m));
return;
}
dfs2 (x + 1, sum), dfs2 (x + 1, (sum + z[x]) % m);
}
int main (void)
{
ios::sync_with_stdio (false), cin.tie (0), cout.tie (0);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> z[i];
dfs1 (1, 0), sort (begin (w), end (w)), dfs2 (n / 2 + 1, 0);
cout << res << '\n';
return 0;
}
D - 周期
题意
给定一个
分析
我们知道,一个有向环存在,当且仅当环上所有边的方向相同。那么,对每个无向环,只有两种情况能存在环。
可能存在多个环。我们设环总共的大小为
即
复杂度:
STD
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
const int N = 2e5 + 10, P = 998244353;
vector<int> e[N];
int dep[N], w[N], nr = 0, tst = 0;
ll fxp (ll a, ll b)
{
ll res = 1;
while (b) {
if (b & 1) res = res * a % P;
b >>= 1, a = a * a % P;
}
return res;
}
void dfs (int u, int fa)
{
dep[u] = dep[fa] + 1;
for (auto v : e[u])
if (!dep[v]) dfs (v, u);
else if (dep[v] > dep[u]) w[nr++] = dep[v] - dep[u] + 1;
}
int main (void)
{
int n;
ll cnt = 0, prd = 1;
ios::sync_with_stdio (false), cin.tie (0), cout.tie (0);
cin >> n;
for (int u = 1, v; u <= n; u++) cin >> v, e[u].push_back (v), e[v].push_back (u);
for (int u = 1; u <= n; u++) if (!dep[u]) dfs (u, 0);
for (int i = 0; i < nr; i++) (cnt += w[i]) %= P, (prd *= (fxp (2, w[i]) + P - 2) % P) %= P;
cout << fxp (fxp (2, cnt), P - 2) * prd % P << '\n';
return 0;
}
F - 人偶
题意
从
分析
最终队列不满的概率大概是
可以直接状压。设
转移即可。
注意边界。当每个物品一定会被选中时候特判。
复杂度:
STD
#include <iostream>
#include <iomanip>
using namespace std;
const int N = 20;
const long double eps = 1e-12;
long double f[1 << N], z[N], res[N];
int main (void)
{
int n, k, cnt = 0;
ios::sync_with_stdio (false), cin.tie (0), cout.tie (0);
cin >> n >> k;
for (int i = 0; i < n; i++) cin >> z[i], cnt += z[i] > eps;
if (cnt <= k) {
for (int i = 0; i < n; i++) cout << (z[i] > eps ? "1.00 " : "0.00 ");
return 0;
}
f[0] = 1;
for (int i = 0; i < 1 << n; i++) {
long double sum = 0;
if (__builtin_popcount (i) == k) {
for (int j = 0; j < n; j++) if (i & (1 << j)) res[j] += f[i];
continue;
}
if (f[i] < eps) continue;
for (int j = 0; j < n; j++) if (i & (1 << j)) sum += z[j];
f[i] /= 1 - sum;
for (int j = 0; j < n; j++) if (!(i & (1 << j))) f[i + (1 << j)] += f[i] * z[j];
}
cout << fixed << setprecision (2);
for (int i = 0; i < n; i++) cout << res[i] << ' ';
return 0;
}
G - 溶解
题意
求
分析
设
其中,由于
就算是转换后,求指数里数也并不简单,况且
至于里头这个大组合数,明显 Lucas。
代码实现不难。
STD
#include <iostream>
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
const ll p[4] = { 2, 13, 2797, 13751 }, T = 1000000222, P = T + 1;
ll a[4], fac[40000], x, y;
static void exgcd (ll a, ll b)
{
ll t;
if (!b) return x = 1, y = 0, void ();
exgcd (b, a % b), t = x, x = y, y = t - a / b * y;
}
static inline ll inv (ll v, ll p) { return exgcd (v, p), (x + p) % p; }
static inline ll C (ll m, ll n, ll p) { return m > n ? 0 : fac[n] * inv (fac[m], p) % p * inv (fac[n - m], p) % p; }
static inline ll lucas (ll m, ll n, ll p) { return m ? lucas (m / p, n / p, p) * C (m % p, n % p, p) % p : 1; }
static ll fxp (ll a, ll b)
{
ll res = 1;
while (b) {
if (b & 1) res = res * a % P;
b >>= 1, a = a * a % P;
}
return res;
}
int main (void)
{
ll res = 0, n, g;
ios::sync_with_stdio (false), cin.tie (0), cout.tie (0);
cin >> n >> g;
if (g % P == 0) cout << "0\n", exit (0);
for (int i = 0; i < 4; i++) {
fac[0] = 1;
for (int j = 1; j <= p[i]; j++) fac[j] = fac[j - 1] * j % p[i];
for (ll q = 1; q * q <= n; q++) {
ll y = q;
if (n % y) continue;
(a[i] += lucas (y, n, p[i])) %= p[i];
if (y * y == n) continue;
(a[i] += lucas (n / y, n, p[i])) %= p[i];
}
}
for (int i = 0; i < 4; i++) (res += T / p[i] * inv (T / p[i], p[i]) % T * a[i] % T) %= T;
cout << fxp (g, res) << '\n';
return 0;
}
G - 水杯
题意
一个长度为
- 单点赋值
- 找
x 个数,每个数删掉一,问能不能重复k 次。分析
本场的数据结构压轴。
比较好分析。设大于等于
在线可以用平衡树,离线可以用树状数组。本来想做强制在线,但是太毒瘤了。(但是赛时还是加了一个在线版本)
复杂度:
STD
#include <iostream>
#include <cstring>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
int fa[N], son[N][2], siz[N], cntq[N], cnt = 1, rt;
ll qsum[N], val[N], z[N];
static inline void maintain (int x) { qsum[x] = qsum[son[x][0]] + qsum[son[x][1]] + val[x] * cntq[x], siz[x] = siz[son[x][0]] + siz[son[x][1]] + cntq[x]; }
static inline int get (int x) { return x == son[fa[x]][1]; }
static inline int compare (int f, int k) { return k > val[f]; }
void rotate (int x)
{
int y = fa[x], z = fa[y], p = get (x);
son[y][p] = son[x][p ^ 1];
if (son[x][p ^ 1]) fa[son[x][p ^ 1]] = y;
fa[fa[son[x][p ^ 1] = y] = x] = z;
if (z) son[z][y == son[z][1]] = x;
maintain (y), maintain (x);
}
int splay (int x, int t = 0)
{
for (int f = fa[x]; (f = fa[x]) != t; rotate (x))
if (fa[f] != t) rotate (get (x) == get (f) ? f : x);
return t ? x : rt = x;
}
int insert_at (int f, ll k)
{
qsum[cnt] = val[cnt] = k, siz[cnt] = cntq[cnt] = 1, fa[cnt] = f;
if (f) son[f][compare (f, k)] = cnt;
return cnt++;
}
int insert (int k)
{
int f = rt, last = 0;
if (!f) return rt = insert_at (0, k);
while (f && val[f] != k) last = f, f = son[f][compare (f, k)];
if (f && val[f] == k) return cntq[f]++, splay (f);
else return f = insert_at (last, k), maintain (last), splay (f);
}
int find (int k)
{
int f = rt;
if (!f) return 0;
while (son[f][compare (f, k)] && val[f] != k) f = son[f][compare (f, k)];
return splay (f);
}
int merge (int x, int y)
{
int f = x;
if (!x || !y) return x + y;
while (son[f][1]) f = son[f][1];
splay (f, fa[x]);
if (y) fa[y] = f;
son[f][1] = y;
return f;
}
int remove (int k)
{
int f = find (k), x;
if (val[f] != k) return 0;
if (cntq[f] > 1) return cntq[f]--, splay (f);
if ((x = merge (son[f][0], son[f][1]))) fa[x] = 0;
return rt = x;
}
int qpre (int k)
{
int f = son[find (k)][0];
if (val[rt] < k) return rt;
while (son[f][1]) f = son[f][1];
return f;
}
int qnext (int k)
{
int f = son[find (k)][1];
if (val[rt] > k) return rt;
while (son[f][0]) f = son[f][0];
return f;
}
int main (void)
{
int n, m;
ios::sync_with_stdio (false), cin.tie (0), cout.tie (0);
cin >> n >> m;
insert (0), insert (1e9);
memset (z, 0x80, sizeof z);
while (m--) {
int t, x, y;
cin >> t >> x >> y;
if (t == 1) z[x] >= 0 ? remove (z[x]) : 0, insert (z[x] = y);
else {
int p = qpre (y), q = qnext (y);
ll sum, k;
splay (p), sum = qsum[p] - qsum[son[p][1]];
splay (q, p), k = cntq[son[q][0]];
splay (q), k += siz[q] - siz[son[q][0]] - 1;
cout << (sum >= (x - k) * y ? "Yes" : "No") << '\n';
}
}
return 0;
}