牛客挑战赛30 简要题解
T1
随便枚举三个位置,然后二维前缀和即可。
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int sum[505][505];
int n, a[505];
ll ans;
int main() {
int i, j, k;
scanf("%d", &n);
for (i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (i = n; i; --i) {
for (j = 1; j <= n; ++j) sum[i][j] = sum[i + 1][j];
for (j = 1; j <= a[i]; ++j) ++sum[i][j];
}
for (i = 1; i <= n; ++i)
for (j = i + 1; j <= n; ++j)
for (k = j + 1; k <= n; ++k)
if (a[i] < a[k] && a[i] < a[j] && a[k] < a[j]) ans += sum[k + 1][a[j] + 1];
printf("%lld\n", ans);
return 0;
}
T2
枚举中位数,把小的设为 \(-1\),大的设为 \(1\),然后前缀和找为 \(0\) 的区间。
被肥鸡卡常了
//time limit exceeded
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn(10005);
const int mod(1e9 + 7);
inline void Inc(int &x, const int y) {
x = x + y >= mod ? x + y - mod : x + y;
}
inline void Dec(int &x, const int y) {
x = x - y < 0 ? x - y + mod : x - y;
}
inline int Add(int x, const int y) {
return x + y >= mod ? x + y - mod : x + y;
}
inline int Sub(int x, const int y) {
return x - y < 0 ? x - y + mod : x - y;
}
inline int Pow(ll x, int y) {
ll ret = 1;
for (; y; y >>= 1, x = x * x % mod)
if (y & 1) ret = ret * x % mod;
return ret;
}
int n, p[maxn], pw[maxn], pw2[maxn], t[maxn << 1], ans, s[maxn];
int main() {
int i, j, ret;
scanf("%d%d", &n, &pw[1]), pw[0] = 1;
for (i = 1; i <= n; ++i) scanf("%d", &p[i]);
for (i = 2; i <= n; ++i) pw[i] = (ll)pw[i - 1] * pw[1] % mod;
for (i = 0; i <= n; ++i) pw2[i] = Pow(pw[i], n);
for (i = 1; i <= n + n; ++i) {
for (j = 1; j <= n; ++j)
if (p[j] + p[j] < i) s[j] = -1;
else if (p[j] + p[j] > i) s[j] = 1;
else s[j] = 0;
t[maxn] = 1, ret = 0;
for (j = 1; j <= n; ++j) {
s[j] += s[j - 1];
Inc(ret, (ll)t[maxn + s[j]] * pw[j] % mod);
Inc(t[s[j] + maxn], pw2[j - 1]);
}
Inc(ans, (ll)ret * i % mod);
for (j = 1; j <= n; ++j) t[s[j] + maxn] = 0;
}
printf("%d\n", ans);
return 0;
}
T3
枚举一个根然后树形 \(DP\),转移是一个组合数。
优化的话就是换根 \(DP\)。
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn(1e5 + 5);
const int mod(998244353);
inline void Inc(int &x, const int y) {
x = x + y >= mod ? x + y - mod : x + y;
}
inline void Dec(int &x, const int y) {
x = x - y < 0 ? x - y + mod : x - y;
}
inline int Add(int x, const int y) {
return x + y >= mod ? x + y - mod : x + y;
}
inline int Sub(int x, const int y) {
return x - y < 0 ? x - y + mod : x - y;
}
inline int Pow(ll x, int y) {
ll ret = 1;
for (; y; y >>= 1, x = x * x % mod)
if (y & 1) ret = ret * x % mod;
return ret;
}
struct Edge { int to, next; };
int n, first[maxn], size[maxn], fac[maxn], inv[maxn], f[maxn], ans, cnt;
Edge edge[maxn << 1];
inline int C(int x, int y) {
if (x < y) return 0;
return (ll)fac[x] * inv[y] % mod * inv[x - y] % mod;
}
inline void AddEdge(int u, int v) {
edge[cnt] = (Edge){v, first[u]}, first[u] = cnt++;
edge[cnt] = (Edge){u, first[v]}, first[v] = cnt++;
}
void Dfs1(int u, int ff) {
int e, v;
size[u] = f[u] = 1;
for (e = first[u]; ~e; e = edge[e].next)
if ((v = edge[e].to) ^ ff) {
Dfs1(v, u);
f[u] = (ll)f[u] * C(size[u] + size[v] - 1, size[v]) % mod * f[v] % mod;
size[u] += size[v];
}
}
void Dfs2(int u, int ff) {
int e, v;
Inc(ans, f[u]);
for (e = first[u]; ~e; e = edge[e].next)
if ((v = edge[e].to) ^ ff) {
f[u] = (ll)f[u] * Pow((ll)C(size[u] - 1, size[v]) * f[v] % mod, mod - 2) % mod;
size[u] -= size[v], size[v] += size[u];
f[v] = (ll)f[v] * C(size[v] - 1, size[u]) % mod * f[u] % mod;
Dfs2(v, u);
f[v] = (ll)f[v] * Pow((ll)C(size[v] - 1, size[u]) * f[u] % mod, mod - 2) % mod;
size[v] -= size[u], size[u] += size[v];
f[u] = (ll)f[u] * C(size[u] - 1, size[v]) % mod * f[v] % mod;
}
}
int main() {
int i, u, v;
memset(first, -1, sizeof(first));
scanf("%d", &n);
for (i = 1; i < n; ++i) scanf("%d%d", &u, &v), AddEdge(u, v);
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for (i = 2; i <= n; ++i) inv[i] = (ll)inv[mod % i] * (mod - mod / i) % mod;
for (i = 2; i <= n; ++i) fac[i] = (ll)fac[i - 1] * i % mod, inv[i] = (ll)inv[i] * inv[i - 1] % mod;
Dfs1(1, 0), Dfs2(1, 0);
printf("%d\n", ans);
return 0;
}
T4
枚举最终的牌和第一种的数目
运用交换求和顺序以及组合恒等式可以得到
考虑求
可以看成是枚举一个点 \((i,n-1),i\in [0,x]\) 作为中转站,从 \((0,0)\) 到 \((i,n-1)\) 再到 \((i,n)\) 再到 \((S,n+m)\) 的格路问题。
也就是 \((0,0)\) 到 \((S,n+m)\) 必须经过端点为 \((0,n),(x,n)\) 的线段。
这个可以用总方案数减去经过 \((x,i)\) 和 \((x+1,i)\) 之间的边的方案数,其中 \(i\in[1,n-1]\)
注意这里是枚举 \(n\),所以可以直接算。
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn(2e7 + 5);
const int mod(998244353);
inline void Inc(int &x, const int y) { x = x + y >= mod ? x + y - mod : x + y; }
inline void Dec(int &x, const int y) { x = x - y < 0 ? x - y + mod : x - y; }
inline int Add(const int x, const int y) { return x + y >= mod ? x + y - mod : x + y; }
inline int Sub(const int x, const int y) { return x - y < 0 ? x - y + mod : x - y; }
inline int Pow(ll x, int y) {
ll ret = 1;
for (; y; y >>= 1, x = x * x % mod)
if (y & 1) ret = ret * x % mod;
return ret;
}
int n, m, s, l, r, f[maxn], g[maxn], inv[maxn];
inline int Calc(int x) {
int i, ret = 0, y = s - x - 1;
if (y < 0) return 0;
f[0] = g[0] = 1;
for (i = 1; i < n; ++i) f[i] = (ll)f[i - 1] * inv[i] % mod * (x + i) % mod;
for (i = 1; i <= m + n; ++i) g[i] = (ll)g[i - 1] * inv[i] % mod * (y + i) % mod;
for (i = 0; i < n; ++i) Dec(ret, (ll)f[i] * g[m + n - i] % mod);
return ret;
}
int main() {
int i;
scanf("%d%d%d%d%d", &n, &m, &s, &l, &r);
inv[0] = inv[1] = 1;
for (i = 2; i <= n + m; ++i) inv[i] = (ll)inv[mod % i] * (mod - mod / i) % mod;
printf("%d\n", Sub(Calc(r), Calc(l - 1)));
return 0;
}
T5
实际就是仙人掌上的所有路径的总长度对 \(Q\) 取模的值。
建立圆方树,树 \(dp\),处理出每个点子树内到它的路径数目。
树边贡献直接算,环边贡献都是一样的,可以考虑枚举端点,也很好算。
两遍树 \(dp\) 即可。
主要代码:头文件太长了
int mod;
inline void inc(int &x, const int y) { x = x + y >= mod ? x + y - mod : x + y; }
inline void dec(int &x, const int y) { x = x - y < 0 ? x - y + mod : x - y; }
inline int add(const int x, const int y) { return x + y >= mod ? x + y - mod : x + y; }
inline int sub(const int x, const int y) { return x - y < 0 ? x - y + mod : x - y; }
inline int fpow(ll x, int y) {
ll ret = 1;
for (; y; y >>= 1, x = x * x % mod)
if (y & 1) ret = ret * x % mod;
return ret;
}
const int maxn(2e5 + 5);
int n, m, tot, dfn[maxn], low[maxn], idx, sta[maxn], top, ans, bel[maxn];
int sum[maxn], fa[maxn], deep[maxn], type[maxn], cir[maxn], f[maxn];
vii graph[maxn];
vi tree[maxn];
inline void addgraph(int u, int v, int w) { graph[u].pb(mp(v, w)), graph[v].pb(mp(u, w)); }
inline void addtree(int u, int v) { tree[u].pb(v), tree[v].pb(u); }
void dfs(int u) {
int v, s;
dfn[u] = low[u] = ++idx, sta[++top] = u;
for (auto cur : graph[u])
if (!dfn[v = cur.first]) {
fa[v] = u, deep[v] = add(deep[u], cur.second);
dfs(v), cmin(low[u], low[v]);
if (low[v] >= dfn[u]) {
++tot, s = 0;
do {
++s, addtree(tot, sta[top--]);
bel[sta[top + 1]] = tot;
} while (sta[top + 1] ^ v);
addtree(tot, u);
if (s == 1) type[tot] = 1, sum[tot] = cur.second;
}
}
else {
cmin(low[u], dfn[v]);
if (low[v] == dfn[u]) sum[bel[v]] = add(cur.second, sub(deep[v], deep[u]));
}
}
void dfs1(int u, int ff) {
int val = type[u] ? 1 : 2;
f[u] = u <= n;
for (int v : tree[u]) if (v ^ ff) dfs1(v, u), inc(f[u], (ll)f[v] * val % mod);
}
void dfs2(int u, int ff) {
int val = type[u] ? 1 : 2, ret = 0, s = 0;
if (u > n) {
for (int v : tree[u]) inc(s, (ll)ret * f[v] % mod), inc(ret, f[v]);
inc(ans, (ll)s * sum[u] % mod);
}
for (int v : tree[u])
if (v ^ ff) {
dec(f[u], (ll)f[v] * val % mod);
inc(f[v], (ll)f[u] * (type[v] ? 1 : 2) % mod);
dfs2(v, u);
dec(f[v], (ll)f[u] * (type[v] ? 1 : 2) % mod);
inc(f[u], (ll)f[v] * val % mod);
}
}
int main() {
int i, u, v, w;
read(n, m, mod), tot = n;
for (i = 1; i <= n; ++i) type[i] = 1;
for (i = 1; i <= m; ++i) read(u, v, w), addgraph(u, v, w);
dfs(1), dfs1(1, 0), dfs2(1, 0), print(ans);
return 0;
}
T6
如果我们能够构造一个多项式 \(H(x)=\prod_{i有二次剩余}(x−i)^k\),那么再将该多项式与 \(F(x)\) 取 \(Gcd\) 可以得到答案多项式。
二次剩余有一个判断式 \(x^{\frac{p−1}{2}}−1\equiv 0(mod~p)\)。
所以 \(x^{\frac{p−1}{2}}−1=\prod_{i有二次剩余}(x-i)(mod~p)\)
只需要先将 \((x^{\frac{p−1}{2}}−1)^k\) 对 \(F(x)\) 取模,接下来两个多项式的次数就都是 \(O(n)\) 级别的。
可以利用快速幂算出 \(x^{\frac{p−1}{2}}\) 和 \((x^{\frac{p−1}{2}}−1)^k\)。
主要代码:
Poly Gcd(Poly x, Poly y) {
if (!y.size()) return x;
Poly z = x % y;
while (z.size() && !z[z.size() - 1]) z.pop_back();
return Gcd(y, z);
}
int n, k;
Poly f, g, pw;
int main() {
int i, x, inv;
scanf("%d%d", &n, &k), f.resize(n + 1);
for (i = 0; i <= n; ++i) scanf("%d", &f[i]);
g.resize(2), g[1] = 1, pw.resize(1), pw[0] = 1;
for (x = (mod - 1) >> 1; x; g = g * g % f, x >>= 1)
if (x & 1) pw = pw * g % f;
Dec(pw[0], 1), g.resize(1), g[0] = 1;
for (x = k; x; pw = pw * pw % f, x >>= 1)
if (x & 1) g = g * pw % f;
g = Gcd(f, g);
printf("%d\n", x = (int)g.size() - 1);
inv = Pow(g[x], mod - 2), g = g * inv;
for (i = 0; i <= x; ++i) printf("%d ", g[i]);
return puts(""), 0;
}