A – 杀毒软件
这套题里面质量最好的题(虽然跟 BZOJ 的某题比较像)。
我们先考虑把所有的病毒串加到 AC 自动机里去,然后再考虑一个 DP 转移,设 \( dp[i][j] \) 为当前第 \(i\) 个字符匹配于自动机节点 \(j\) 上的方案数。发现数据范围很小,所以其实这个可以矩阵优化。建完 AC 自动机之后,我们针对 \(a_i = 0, 1, -1\) 算三个矩阵。算完这个之后用线段树来维护就可以了。(复杂度上天了)
实测矩阵乘法的时候剪枝可以快 10s 的样子。
// GuOJ1126.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 200, MAX_S = 18, mod = 998244353;
int n, m, q, s, seq[MAX_N], idx[MAX_S], rk[MAX_S], ptot;
int ch[MAX_N][2], fail[MAX_N];
bool tag[MAX_N];
char str[MAX_N];
struct matrix
{
int mat[MAX_S][MAX_S];
int *operator[](const int &rhs) { return mat[rhs]; }
void clear() { memset(mat, 0, sizeof(mat)); }
matrix operator*(const matrix &rhs)
{
matrix ret;
ret.clear();
for (int i = 0; i <= s; i++)
for (int k = 0; k <= s; k++)
if (mat[i][k])
for (int j = 0; j <= s; j++)
if (rhs.mat[k][j])
ret[i][j] = (0LL + ret[i][j] + 1LL * mat[i][k] * rhs.mat[k][j] % mod) % mod;
return ret;
}
} nodes[MAX_N << 2], t1, t0, tr;
#define lson (p << 1)
#define rson ((p << 1) | 1)
#define mid ((l + r) >> 1)
void build(int l, int r, int p)
{
if (l == r)
{
if (seq[l] == -1)
nodes[p] = tr;
else if (seq[l] == 0)
nodes[p] = t0;
else
nodes[p] = t1;
return;
}
build(l, mid, lson), build(mid + 1, r, rson);
nodes[p] = nodes[lson] * nodes[rson];
}
void update(int qx, int l, int r, int p, int val)
{
if (l == r)
{
if (val == 0)
nodes[p] = t0;
else if (val == 1)
nodes[p] = t1;
else
nodes[p] = tr;
return;
}
if (qx <= mid)
update(qx, l, mid, lson, val);
else
update(qx, mid + 1, r, rson, val);
nodes[p] = nodes[lson] * nodes[rson];
}
matrix query(int ql, int qr, int l, int r, int p)
{
if (ql <= l && r <= qr)
return nodes[p];
bool tag1 = ql <= mid, tag2 = mid < qr;
if (tag1 && tag2)
return query(ql, qr, l, mid, lson) * query(ql, qr, mid + 1, r, rson);
if (tag1)
return query(ql, qr, l, mid, lson);
else
return query(ql, qr, mid + 1, r, rson);
}
void insert()
{
int len = strlen(str + 1), p = 0;
for (int i = 1; i <= len; i++)
{
if (ch[p][str[i] - '0'] == 0)
ch[p][str[i] - '0'] = ++ptot;
p = ch[p][str[i] - '0'];
}
tag[p] = true;
}
void build()
{
queue<int> q;
for (int i = 0; i < 2; i++)
if (ch[0][i])
q.push(ch[0][i]);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0; i < 2; i++)
if (ch[u][i] == 0)
ch[u][i] = ch[fail[u]][i];
else
{
fail[ch[u][i]] = ch[fail[u]][i];
tag[ch[u][i]] |= tag[fail[ch[u][i]]];
q.push(ch[u][i]);
}
}
}
int main()
{
// ptot = 2, ch[0][0] = 1, ch[0][1] = 2;
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
scanf("%d", &seq[i]);
for (int i = 1; i <= m; i++)
scanf("%s", str + 1), insert();
build();
for (int i = 0; i <= ptot; i++)
if (!tag[i])
idx[++s] = i, rk[i] = s;
for (int i = 0; i <= s; i++)
{
int id = idx[i], lc = rk[ch[id][0]], rc = rk[ch[id][1]];
if (tag[id])
continue;
if (!tag[ch[id][0]])
tr[i][lc]++, t0[i][lc]++;
if (!tag[ch[id][1]])
tr[i][rc]++, t1[i][rc]++;
}
build(1, n, 1);
while (q--)
{
int x, y;
scanf("%s%d%d", str + 1, &x, &y);
if (str[1] == 'Q')
{
matrix src = query(x, y, 1, n, 1);
int ans = 0;
for (int i = 0; i <= s; i++)
ans = (0LL + ans + src[0][i]) % mod;
printf("%d\n", ans);
}
else
{
update(x, 1, n, 1, y);
seq[x] = y;
}
}
return 0;
}
B – 政治正确
什么狗屎题?
// GuOJ1127.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 200;
int n, md;
double v[MAX_N];
struct vec
{
double d[10];
void clear() { memset(d, 0, sizeof(d)); }
double &operator[](const int &rhs) { return d[rhs]; }
vec operator+(const vec &rhs)
{
vec ret;
ret.clear();
for (int i = 0; i < md; i++)
ret[i] = d[i] + rhs.d[i];
return ret;
}
vec operator-(const vec &rhs)
{
vec ret;
ret.clear();
for (int i = 0; i < md; i++)
ret[i] = d[i] - rhs.d[i];
return ret;
}
vec operator*(const double &rhs)
{
vec ret;
ret.clear();
for (int i = 0; i < md; i++)
ret[i] = d[i] * rhs;
return ret;
}
vec operator/(const double &rhs)
{
vec ret;
ret.clear();
for (int i = 0; i < md; i++)
ret[i] = d[i] / rhs;
return ret;
}
vec operator-(void)
{
vec ret;
ret.clear();
for (int i = 0; i < md; i++)
ret[i] = -d[i];
return ret;
}
double len2()
{
double ret = 0;
for (int i = 0; i < md; i++)
ret += d[i] * d[i];
return ret;
}
vec normalize()
{
double t = sqrt(len2());
return (*this) / t;
}
void scan()
{
for (int i = 0; i < md; i++)
scanf("%lf", &d[i]);
}
void print()
{
for (int i = 0; i < md; i++)
printf("%lf", d[i]);
}
} pts[MAX_N];
vec under(vec &rhs) { return -rhs * (1 / (1 - rhs.len2())); }
vec centralize(vec &rhs)
{
vec ret;
ret.clear();
for (int i = 1; i <= n; i++)
{
double len2 = (pts[i] - rhs).len2();
if (len2 != 0)
ret = ret + (rhs - pts[i]) * (v[i] / len2);
}
return ret;
}
int main()
{
scanf("%d%d", &n, &md);
for (int i = 1; i <= n; i++)
pts[i].scan();
double step = 0.5, rot = 0.8;
vec ret;
ret.clear();
for (int rd = 1; rd <= 50; rd++)
{
vec c = under(ret) + centralize(ret);
vec roted = c.normalize() * step;
if ((ret + roted).len2() < 1)
ret = ret + roted;
step *= rot;
}
ret.print();
return 0;
}
C – 水果拼盘
傻逼题,直接算每个水果的轮数,然后用组合数分开来计数即可。
// GuOJ1128.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 100010, mod = 998244353;
int n, m, k, rdcnt[MAX_N], fac[MAX_N], fac_inv[MAX_N], ai[MAX_N], bi[MAX_N];
int fpow(int bas, int tim)
{
int ret = 1;
while (tim)
{
if (tim & 1)
ret = 1LL * ret * bas % mod;
bas = 1LL * bas * bas % mod;
tim >>= 1;
}
return ret;
}
int binomial(int n_, int k_) { return n_ >= k_ ? 1LL * fac[n_] * fac_inv[k_] % mod * fac_inv[n_ - k_] % mod : 0; }
int main()
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= m; i++)
scanf("%d", &ai[i]);
for (int i = 1; i <= m; i++)
scanf("%d", &bi[i]);
for (int i = fac[0] = 1; i <= n; i++)
fac[i] = 1LL * fac[i - 1] * i % mod;
fac_inv[0] = fac_inv[1] = 1;
for (int i = 2; i <= n; i++)
fac_inv[i] = 1LL * (mod - mod / i) * fac_inv[mod % i] % mod;
for (int i = 2; i <= n; i++)
fac_inv[i] = 1LL * fac_inv[i - 1] * fac_inv[i] % mod;
for (int i = 1, ktot, val; i <= n; i++)
{
scanf("%d", &ktot);
while (ktot--)
scanf("%d", &val), rdcnt[val]++;
}
int ans = 0, inv = fpow(binomial(n, k), mod - 2);
for (int i = 1; i <= m; i++)
{
int prob = 1LL * binomial(n - rdcnt[i], k) * inv % mod;
ans = (0LL + ans + 1LL * prob * bi[i] % mod + 1LL * (1 + mod - prob) % mod * ai[i] % mod) % mod;
}
printf("%d\n", ans);
return 0;
}