2021牛客暑期多校训练营9
C、Cells
LGV引理代进去算算,会发现是个范德蒙德行列式。
于是问题转化为快速求出一个集合中任意两个元素差的乘积。然后赛场上卡了。
考虑到元素范围在 1 0 6 10^6 106,发现元素差的取值范围不大,于是可以计算每个差对答案的贡献,即每个差会被乘几次。于是可以用生成函数的考虑方法,把元素放在指数上,前面的系数表示个数。那么两个生成函数一乘就能算出来取 α i + α j \alpha_i+\alpha_j αi+αj的取法种数。而这题要求差,所以把 α j \alpha_j αj换成 − α j -\alpha_j −αj即可。由于ntt没办法算负指数,可以统一加一个偏移量计算。
最后统计答案即可。
#include <bits/stdc++.h>
typedef long long ll;
const int MAXN = 5e6 + 10, MOD = 998244353;
int w[MAXN];
ll inv[MAXN], fac[MAXN];
ll qpow(ll a, ll b)
{
ll res = 1; a %= MOD;
while (b)
{
if (b & 1) res = res * a % MOD;
a = a * a % MOD; b >>= 1;
}
return res;
}
ll a[MAXN], b[MAXN], rev[MAXN], k;
const int UP = 1000001;
int qpow(int a, int b)
{
int res = 1; a %= MOD;
while (b)
{
if (b & 1) res = 1ll * res * a % MOD;
a = 1ll * a * a % MOD; b >>= 1;
}
return res;
}
void ntt(ll *a, int n, int opt)
{
for (int i = 0; i < n; ++i)
if (i < rev[i]) std::swap(a[i], a[rev[i]]);
for (int h = 2; h <= n; h <<= 1)
{
int gn = qpow(3, (MOD - 1) / h);
if (opt < 0) gn = qpow(gn, MOD - 2);
for (int j = 0, g = 1; j < n; j += h, g = 1)
for (int k = j; k < j + h / 2; ++k, g = 1ll * g * gn % MOD)
{
int u = a[k], v = 1ll * g * a[k + h / 2] % MOD;
a[k] = (u + v) % MOD; a[k + h / 2] = (u - v) % MOD;
}
}
if (opt < 0)
{
ll inv = qpow(n, MOD - 2);
for (int i = 0; i < n; ++i)
a[i] = 1ll * a[i] * inv % MOD;
}
}
int main()
{
int n; scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &w[i]), w[i]++;
fac[0] = inv[0] = 1;
for (int i = 1; i <= n; ++i)
{
fac[i] = fac[i - 1] * i % MOD;
inv[i] = inv[i - 1] * qpow(i, MOD - 2) % MOD;
}
ll res = 1;
for (int i = 1; i <= n; ++i)
res = res * inv[i] % MOD;
for (int i = 1; i <= n; ++i)
res = res * w[i] % MOD;
for (int i = 1; i <= n; ++i)
a[w[i]]++, b[UP - w[i]]++;
int tot = 1, k = 0;
while (tot <= 2 * UP + 1)
++k, tot <<= 1;
for (int i = 1; i <= tot; ++i)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << k - 1);
ntt(a, tot, 1); ntt(b, tot, 1);
for (int i = 0; i <= tot; ++i)
a[i] = a[i] * b[i] % MOD;
ntt(a, tot, -1);
for (int i = 0; i <= tot; ++i)
a[i] = (a[i] + MOD) % MOD;
for (int i = UP + 1; i <= 2 * UP; ++i)
res = res * qpow(ll(i - UP), a[i]) % MOD;
printf("%lld\n", res);
return 0;
}
E、Eyjafjalla
倍增找到祖先中深度最小的小于等于 r r r的点,然后询问该子树中大于等于 l l l的点有多少个即可。
主席树瞎写写就过了。
#include <bits/stdc++.h>
typedef long long ll;
const int MAXN = 3e5 + 10, INF = 0x3f3f3f3f;
std::vector < int > G[MAXN], alls;
int n, m, t[MAXN], fa[MAXN][32], dist[MAXN], vis[MAXN], sz[MAXN];
int que[MAXN], l, r, root[MAXN], id[MAXN];
int cnt;
struct node
{
int lson, rson, val;
}tr[MAXN * 40];
struct Query
{
int root, l, r;
}Q[MAXN];
int idx = 0;
int getidx(int x)
{
return std::lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;
}
int build(int l, int r)
{
if (l == r) return ++idx;
int p = ++idx, mid = l + r >> 1;
tr[p].lson = build(l, mid); tr[p].rson = build(mid + 1, r);
return p;
}
int insert(int now, int prv, int l, int r)
{
int pidx = ++idx;
tr[pidx].val = tr[prv].val + 1;
if (l == r) return pidx;
int mid = l + r >> 1;
if (now <= mid) tr[pidx].lson = insert(now, tr[prv].lson, l, mid), tr[pidx].rson = tr[prv].rson;
else tr[pidx].lson = tr[prv].lson, tr[pidx].rson = insert(now, tr[prv].rson, mid + 1, r);
return pidx;
}
int query(int p, int q, int t, int s, int l, int r)
{
if (l <= t && s <= r) return tr[q].val - tr[p].val;
int mid = t + s >> 1, ans = 0;
if (l <= mid) ans += query(tr[p].lson, tr[q].lson, t, mid, l, r);
if (r > mid) ans += query(tr[p].rson, tr[q].rson, mid + 1, s, l, r);
return ans;
}
void dfs(int now, int f)
{
sz[now] = 1; ++cnt;
root[cnt] = insert(getidx(t[now]), root[cnt - 1], 1, m);
id[now] = cnt;
for (auto &i : G[now])
{
if (i == f) continue;
dfs(i, now);
sz[now] += sz[i];
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n - 1; ++i)
{
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v); G[v].push_back(u);
}
t[0] = INF;
for (int i = 1; i <= n; ++i)
scanf("%d", &t[i]), alls.push_back(t[i]);
int q; scanf("%d", &q);
for (int i = 1; i <= q; ++i)
{
scanf("%d%d%d", &Q[i].root, &Q[i].l, &Q[i].r);
alls.push_back(Q[i].l); alls.push_back(Q[i].r);
}
std::sort(alls.begin(), alls.end());
alls.erase(std::unique(alls.begin(), alls.end()), alls.end());
m = alls.size();
dist[1] = 1, vis[1] = 1;
que[1] = 1, l = r = 1;
while (l <= r)
{
int u = que[l++];
for (auto &i : G[u])
if (!vis[i])
{
dist[i] = dist[u] + 1, fa[i][0] = u;
que[++r] = i, vis[i] = 1;
for (int j = 1; j <= 31; ++j)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
}
root[0] = build(1, m); dfs(1, -1);
for (int i = 1; i <= q; ++i)
{
int rx = Q[i].root;
if (t[rx] > Q[i].r || t[rx] < Q[i].l)
{
printf("0\n");
continue;
}
for (int j = 31; j >= 0; --j)
if (t[fa[rx][j]] <= Q[i].r) rx = fa[rx][j];
printf("%d\n", query(root[id[rx] - 1], root[id[rx] + sz[rx] - 1], 1, m, getidx(Q[i].l), getidx(Q[i].r)));
}
return 0;
}
H、Happy Number
看起来就和三进制有关系,但这个三进制的数值表示是 1 1 1到 3 3 3。把 n n n转换为三进制之后对于每个小于等于 0 0 0的位向高位借一个 3 3 3即可。
#include <bits/stdc++.h>
typedef long long ll;
int main()
{
int n; scanf("%d", &n);
std::vector < int > a;
while (n)
a.push_back(n % 3), n /= 3;
for (int i = 0; i < (int)a.size() - 1; ++i)
if (a[i] <= 0) a[i] += 3, a[i + 1]--;
while (!a.back()) a.pop_back();
std::reverse(a.begin(), a.end());
for (auto &i : a)
if (i == 1) printf("2");
else if (i == 2) printf("3");
else printf("6");
printf("\n");
return 0;
}