HDU 多校 2024 Round 1
懒的叫他原来的那个名字了。vp 队友是 zrt 和 zys。做了 10/12 个题,还有一个题没调出来。总的来说代码实现能力偏弱,三人三机才勉强打完。
- A 循环位移
直接哈希,把哈希值记录在一个哈希表里,查询时查询某个子段是否在哈希表即可。
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 2049000;
const ull P = 131;
template <typename T>
void read(T &x)
{
T sgn = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= sgn;
}
ull pw[maxn], hsh[maxn];
char a[maxn], b[maxn];
map<ull, int> mp;
ull get(int l, int r)
{
return hsh[r] - hsh[l - 1] * pw[r - l + 1];
}
int main()
{
int T;
read(T);
while (T--)
{
scanf("%s%s", a + 1, b + 1);
int n = strlen(a + 1), m = strlen(b + 1);
pw[0] = 1;
for (int i = 1; i <= max(2 * n, m); i++) pw[i] = pw[i - 1] * P;
for (int i = 1; i <= n; i++) a[i + n] = a[i];
for (int i = 1; i <= 2 * n; i++)
{
hsh[i] = hsh[i - 1] * P + (a[i] - 'A' + 1);
}
mp.clear();
for (int i = 1; i <= n; i++)
{
mp[get(i, i + n - 1)] = 1;
}
for (int i = 1; i <= m; i++)
{
hsh[i] = hsh[i - 1] * P + (b[i] - 'A' + 1);
}
int ans = 0;
for (int i = 1; i + n - 1 <= m; i++)
{
if (mp.find(get(i, i + n - 1)) != mp.end()) ans++;
}
printf("%d\n", ans);
}
return 0;
}
- B 星星
看清数据范围,这个范围应该就是暴力。直接 dp 可以过。
随机化一下复杂度变成 \(O(n\sqrt{K})\)(根据随机游走的理论)
#include <bits/stdc++.h>
using namespace std;
template <typename T>
void read(T &x)
{
T sgn = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= sgn;
}
int n, k, a[1005][5];
long long dp[4005];
void MAIN()
{
read(n); read(k);
int B = 33;
for (int i = 1; i <= k; i++) dp[i] = 1e18;
for (int i = 1; i <= n; i++) for (int j = 1; j <= 4; j++) read(a[i][j]);
for (int i = 1; i <= n; i++)
{
int now = 1ll * i * k / n;
for (int j = now + 4 * B; j >= max(now - 4 * B, 1); j--)
for (int o = 1; o <= min(j, 4); o++)
dp[j] = min(dp[j], dp[j - o] + a[i][o]);
}
printf("%lld\n", dp[k]);
}
int main()
{
int T;
read(T);
while (T--) MAIN();
return 0;
}
- C 树
分类讨论:\(\max(a_u,a_v)^2-a_u\times a_v\)。后者容易维护,前者考虑线段树合并的时候,统计一个平方和以及个数即可。
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 1e6 + 5;
template <typename T>
void read(T &x)
{
T sgn = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= sgn;
}
bool _;
int n, a[maxn];
vector<int> vec[maxn];
ull ans[maxn], s[maxn], ss[maxn], fans, sumA[maxn * 20], res[maxn * 20];
int rt[maxn], tot, ls[maxn * 20], rs[maxn * 20], sum[maxn * 20];
int MAX = 1e6;
bool __;
void ins(int &p, int l, int r, int x)
{
if (!p) p = ++tot;
sum[p]++;
sumA[p] += (ull)x * x;
if (l == r) return;
int mid = (l + r) >> 1;
if (x <= mid) ins(ls[p], l, mid, x);
else ins(rs[p], mid + 1, r, x);
}
int Merge(int u, int v, int l, int r)
{
if (!u || !v) return u | v;
sum[u] += sum[v];
sumA[u] += sumA[v];
if (l == r)
{
res[u] = (ull)l * l * (1ll * sum[u] * (sum[u] - 1) / 2);
return u;
}
int mid = (l + r) >> 1;
ls[u] = Merge(ls[u], ls[v], l, mid);
rs[u] = Merge(rs[u], rs[v], mid + 1, r);
res[u] = res[ls[u]] + res[rs[u]] + sumA[rs[u]] * sum[ls[u]];
return u;
}
void dfs(int u, int fa)
{
s[u] = a[u];
ss[u] = (ull)a[u] * a[u];
ins(rt[u], 1, MAX, a[u]);
for (int v : vec[u]) if (v != fa)
{
dfs(v, u);
s[u] += s[v];
ss[u] += ss[v];
rt[u] = Merge(rt[u], rt[v], 1, MAX);
}
ans[u] -= (s[u] * s[u] - ss[u]) / 2;
ans[u] += res[rt[u]];
ans[u] *= 2;
fans ^= ans[u];
}
void MAIN()
{
read(n);
for (int i = 1, u, v; i < n; i++)
{
read(u); read(v);
vec[u].push_back(v);
vec[v].push_back(u);
}
for (int i = 1; i <= n; i++) read(a[i]);
dfs(1, 0);
cout << fans << "\n";
}
int main()
{
int T = 1;
while (T--) MAIN();
return 0;
}
- D 传送
很明显的线段树分治,考虑到底的时候要给 \(1\) 所在的连通块打 tag,那写一个带权可撤销并查集即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 6e5 + 5;
typedef long long ll;
template <typename T>
void read(T &x)
{
T sgn = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= sgn;
}
int n, m, N = 1;
ll val[maxn];
int fa[maxn], sz[maxn], stk[maxn], top;
vector<pair<int, int>> vec[maxn << 2];
int Find(int x) { return x == fa[x] ? x : Find(fa[x]); }
void pusht(int p, int v) { val[p] += v; }
void Merge(int x, int y)
{
int fx = Find(x), fy = Find(y);
if (fx == fy) return;
if (sz[fx] > sz[fy])
swap(fx, fy);
fa[fx] = fy;
sz[fy] += sz[fx];
stk[++top] = fx;
val[fx] -= val[fy];
}
void undo(int goal)
{
while (top > goal)
{
int x = stk[top], y = fa[x];
sz[y] -= sz[x];
val[x] += val[y];
fa[x] = x;
top--;
}
}
void modify(int x, int y, int u, int v)
{
for (int lc = x - 1 + N, rc = y + 1 + N; lc ^ rc ^ 1; lc >>= 1, rc >>= 1)
{
if (~lc & 1) vec[lc ^ 1].emplace_back(u, v);
if (rc & 1) vec[rc ^ 1].emplace_back(u, v);
}
}
void work(int p, int l, int r, int x, int y)
{
if (l > y || r < x) return;
int tmp = top;
for (auto x : vec[p]) Merge(x.first, x.second);
if (l == r)
{
pusht(Find(1), l);
undo(tmp);
return;
}
int mid = (l + r) >> 1;
work(p << 1, l, mid, x, y);
work(p << 1 | 1, mid + 1, r, x, y);
undo(tmp);
}
int main()
{
read(n); read(m);
while (N <= n + 1) N <<= 1;
for (int i = 1; i <= n; i++) fa[i] = i, sz[i] = 1;
for (int i = 1, u, v, l, r; i <= m; i++)
{
read(u); read(v); read(l); read(r);
modify(l, r, u, v);
}
work(1, 0, N - 1, 1, n);
ll ans = 0;
for (int i = 1; i <= n; i++) ans ^= val[Find(i)];
printf("%lld\n", ans);
return 0;
}
- E 博弈
先考虑总个数为偶数的情况,此时两个人的字符串长度是一样的,就有可能产生平局的情况。考虑一个双射,如果 \(A<B\),那么两个人交换字符串,就是 \(B<A\)。所以概率就是 \(\dfrac{1-p}{2}\),其中 \(p\) 是平局的概率。对于奇数,就是 \(\frac{1+p}{2}\),但是要枚举最后一个字符是什么。
接下来就变成了同一个问题,计算平局。那平局的要求显然就是每个字符都是偶数,并且成双出现。记 \(c_i\) 是第 \(i\) 个字符出现次数,\(m=n/2\)。那么答案应该是 \(\dfrac{m!}{\prod (\frac{c_i}{2})!}\)。
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353, maxn = 1e7 + 5;
int qmod(int x) { return x >= mod ? x - mod : x; }
int ksm(int a, int b = mod - 2)
{
int ret = 1;
for (; b; b >>= 1, a = 1ll * a * a % mod) if (b & 1) ret = 1ll * ret * a % mod;
return ret;
}
template <typename T>
void read(T &x)
{
T sgn = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= sgn;
}
int n;
int fac[maxn], ifac[maxn], cnt[26];
void init(int N)
{
fac[0] = ifac[0] = 1;
for (int i = 1; i <= N; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[N] = ksm(fac[N]);
for (int i = N - 1; i; i--) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
}
int calc(int tot, int id)
{
int flg = 1;
for (int i = 0; i < 26; i++) if (cnt[i] & 1)
{
flg = 0;
}
if (flg)
{
int P = fac[tot / 2], Q = ifac[tot];
for (int i = 0; i < 26; i++)
{
P = 1ll * P * ifac[cnt[i] / 2] % mod;
Q = 1ll * Q * fac[cnt[i]] % mod;
}
int draw = 1ll * P * Q % mod;
if (!id) return 1ll * ksm(2) * qmod(1 - draw + mod) % mod;
else return qmod(1ll * ksm(2) * qmod(1 - draw + mod) % mod + draw);
}
else
{
return ksm(2);
}
}
int main()
{
init(10000000);
int T;
read(T);
while (T--)
{
read(n);
for (int i = 0; i < 26; i++) cnt[i] = 0;
for (int i = 1; i <= n; i++)
{
char op[5];
scanf("%s", op + 1);
read(cnt[op[1] - 'a']);
}
int tot = 0;
for (int i = 0; i < 26; i++) tot += cnt[i];
if (tot & 1)
{
int ans = 0;
for (int i = 0; i < 26; i++)
{
int P = 1ll * cnt[i] * ksm(tot) % mod;
cnt[i]--;
ans = qmod(ans + 1ll * P * calc(tot - 1, 1) % mod);
cnt[i]++;
}
printf("%d\n", ans);
}
else
{
printf("%d\n", calc(tot, 0));
}
}
return 0;
}
- F 序列立方
立方值的和,可以拆成三个出现位置。于是考虑 dp,记 \(f_{i,j,k}\) 代表以 \(i,j,k\) 结尾的子序列相同的个数。前缀和优化转移。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 255, mod = 998244353;
int qmod(int x) { return x >= mod ? x - mod : x; }
int ksm(int a, int b = mod - 2)
{
int ret = 1;
for (; b; b >>= 1, a = 1ll * a * a % mod) if (b & 1) ret = 1ll * ret * a % mod;
return ret;
}
int n, a[maxn], f[maxn][maxn][maxn], g[maxn][maxn][maxn];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int ans = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
for (int k = 1; k <= n; k++)
{
if (a[i] == a[j] && a[j] == a[k])
{
f[i][j][k] = qmod(1 + g[i - 1][j - 1][k - 1]);
if (f[i][j][k])
{
ans = qmod(ans + f[i][j][k]);
}
}
g[i][j][k] = f[i][j][k];
g[i][j][k] = qmod(g[i][j][k] + g[i - 1][j][k]);
g[i][j][k] = qmod(g[i][j][k] + g[i][j - 1][k]);
g[i][j][k] = qmod(g[i][j][k] + g[i][j][k - 1]);
g[i][j][k] = qmod(g[i][j][k] - g[i - 1][j - 1][k] + mod);
g[i][j][k] = qmod(g[i][j][k] - g[i - 1][j][k - 1] + mod);
g[i][j][k] = qmod(g[i][j][k] - g[i][j - 1][k - 1] + mod);
g[i][j][k] = qmod(g[i][j][k] + g[i - 1][j - 1][k - 1]);
}
}
}
printf("%d\n", ans);
return 0;
}
- G 三元环
考虑容斥,不是三元环的一定有一个点有两个入点,所以求出每个点的入读即可。也就是一个三维偏序,直接写一个 cdq 即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5, mod = 998244353;
template <typename T>
void read(T &x)
{
T sgn = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= sgn;
}
int qmod(int x) { return x >= mod ? x - mod : x; }
int ksm(int a, int b = mod - 2)
{
int ret = 1;
for (; b; b >>= 1, a = 1ll * a * a % mod) if (b & 1) ret = 1ll * ret * a % mod;
return ret;
}
int n, a[maxn], b[maxn], f[maxn], g[maxn], h[maxn], k[maxn], rk[maxn];
int c[maxn], tmp[maxn];
void add(int x, int v)
{
while (x <= n) a[x] += v, x += x & -x;
}
int qry(int x)
{
int res = 0;
while (x)
{
res += a[x];
x -= x & -x;
}
return res;
}
int stk[maxn], top;
bool cmp1(int x, int y)
{
return b[x] < b[y];
}
bool cmp2(int x, int y)
{
return b[x] > b[y];
}
void cdq1(int l, int r)
{
if (l == r) return;
int mid = (l + r) >> 1;
cdq1(l, mid);
cdq1(mid + 1, r);
top = 0;
for (int i = mid + 1, j = l - 1; i <= r; i++)
{
while (j < mid && b[rk[j + 1]] < b[rk[i]])
{
j++;
add(c[rk[j]], 1);
stk[++top] = c[rk[j]];
}
f[rk[i]] += qry(c[rk[i]] - 1);
}
for (int i = 1; i <= top; i++) add(stk[i], -1);
for (int i = l; i <= r; i++) tmp[i] = rk[i];
int p = l, q = mid + 1, now = l;
while (p <= mid || q <= r)
{
if (q > r || (p <= mid && b[tmp[p]] < b[tmp[q]])) rk[now++] = tmp[p++];
else rk[now++] = tmp[q++];
}
}
void cdq2(int l, int r)
{
if (l == r) return;
int mid = (l + r) >> 1;
cdq2(l, mid);
cdq2(mid + 1, r);
top = 0;
for (int i = l, j = mid; i <= mid; i++)
{
while (j < r && b[rk[j + 1]] > b[rk[i]])
{
j++;
add(n - c[rk[j]] + 1, 1);
stk[++top] = n - c[rk[j]] + 1;
}
g[rk[i]] += qry(n - c[rk[i]]);
}
for (int i = 1; i <= top; i++) add(stk[i], -1);
for (int i = l; i <= r; i++) tmp[i] = rk[i];
int p = l, q = mid + 1, now = l;
while (p <= mid || q <= r)
{
if (q > r || (p <= mid && b[tmp[p]] > b[tmp[q]])) rk[now++] = tmp[p++];
else rk[now++] = tmp[q++];
}
}
mt19937 rnd(time(0));
int main()
{
read(n);
for (int i = 1; i <= n; i++) read(b[i]);
for (int i = 1; i <= n; i++) read(c[i]);
ll ans = 1ll * n * (n - 1) * (n - 2) / 6;
for (int i = 1; i <= n; i++) rk[i] = i;
cdq1(1, n);
for (int i = 1; i <= n; i++) rk[i] = i;
cdq2(1, n);
for (int i = 1; i <= n; i++)
{
ll d = f[i] + n - i - g[i];
ans -= d * (d - 1) / 2;
}
printf("%lld\n", ans);
return 0;
}
- H 位运算
位运算卷积板子题。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = (1 << 15) | 5;
template <typename T>
void read(T &x)
{
T sgn = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= sgn;
}
ll A[maxn], B[maxn], C[maxn], D[maxn];
void fmt_or(ll *f, ll len, ll op)
{
for (ll i = 1; i < len; i <<= 1)
for (ll j = 0; j < len; j += i << 1)
for (ll k = 0; k < i; k++)
f[j + k + i] += op * f[j + k];
}
void fmt_and(ll *f, ll len, ll op)
{
for (ll i = 1; i < len; i <<= 1)
for (ll j = 0; j < len; j += i << 1)
for (ll k = 0; k < i; k++)
f[j + k] += op * f[j + k + i];
}
void fmt_xor(ll *f, ll len, ll op)
{
for (ll i = 1; i < len; i <<= 1)
for (ll j = 0; j < len; j += i << 1)
for (ll k = 0; k < i; k++)
{
ll x = f[j + k], y = f[j + k + i];
if (op == 1) f[j + k] = x + y, f[j + k + i] = x - y;
else f[j + k] = (x + y) / 2, f[j + k + i] = (x - y) / 2;
}
}
int main()
{
ll T;
read(T);
while (T--)
{
ll n, k;
read(n); read(k);
for (ll i = 0; i < (1 << k); i++)
A[i] = 1, B[i] = 1, C[i] = 1, D[i] = 1;
fmt_and(A, 1 << k, 1);
fmt_and(B, 1 << k, 1);
for (ll i = 0; i < (1 << k); i++) A[i] = A[i] * B[i];
fmt_and(A, 1 << k, -1);
fmt_xor(A, 1 << k, 1);
fmt_xor(C, 1 << k, 1);
for (ll i = 0; i < (1 << k); i++) A[i] = A[i] * C[i];
fmt_xor(A, 1 << k, -1);
fmt_or(A, 1 << k, 1);
fmt_or(D, 1 << k, 1);
for (ll i = 0; i < (1 << k); i++) A[i] = A[i] * D[i];
fmt_or(A, 1 << k, -1);
printf("%lld\n", A[n]);
}
return 0;
}

浙公网安备 33010602011771号