2023-11 习题选讲
SHA256: f747ba37
有 \(n\) 个字符串,每个字符串都由 \(len\) 个小写字符组成。
现在要进行 \(m\) 次操作,每次操作会给出 \(x,l,r,c\) ,
你需要将第 \(x\) 个串的第 \(l\) 个字符到第 \(r\) 个字符都改为字符 \(c\) 。
每次操作结束后,你需要告诉现在一共有多少种不同的字符串。
区间染色想到开线段树。
查询有多少种字符串需要哈希。
所以要线段树维护哈希值。
const int N = 1e5 + 5;
const int V = 5e5 + 5;
const int B = 3331;
int n, len, m;
int a[N];
ull pw[N], ps[N];
ull get(int l, int r, int c) {
return (ps[r] - ps[l - 1]) * c;
}
struct SgT {
vector<ull> f;
vector<int> t, le, ri;
SgT(int sz) {
sz += 5;
f.resize(sz << 2);
t.resize(sz << 2);
le.resize(sz << 2);
ri.resize(sz << 2);
}
void pushup(int u) {
f[u] = f[u << 1] + f[u << 1 | 1];
}
void pushdown(int u) {
if(t[u]) {
f[u << 1] = get(le[u << 1], ri[u << 1], t[u]);
t[u << 1] = t[u];
f[u << 1 | 1] = get(le[u << 1 | 1], ri[u << 1 | 1], t[u]);
t[u << 1 | 1] = t[u];
t[u] = 0;
}
}
void build(int u, int l, int r) {
le[u] = l; ri[u] = r;
if(l == r) {
f[u] = a[l] * pw[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int c) {
if(l <= le[u] && ri[u] <= r) {
f[u] = get(le[u], ri[u], c);
t[u] = c;
return;
}
pushdown(u);
int mid = le[u] + ri[u] >> 1;
if(l <= mid) modify(u << 1, l, r, c);
if(mid < r) modify(u << 1 | 1, l, r, c);
pushup(u);
}
ull query(int u, int l, int r) {
if(l <= le[u] && ri[u] <= r) {
return f[u];
}
pushdown(u);
int mid = le[u] + ri[u] >> 1;
ull res = 0;
if(l <= mid) res += query(u << 1, l, r);
if(mid < r) res += query(u << 1 | 1, l, r);
return res;
}
};
struct MapSet {
map<ull, int> mp;
int res;
void insert(ull x) {
if(!mp[x]) res++;
mp[x]++;
}
void erase(ull x) {
mp[x]--;
if(!mp[x]) res--;
}
int size() {
return res;
}
} S;
void solve() {
cin >> n >> len >> m;
pw[0] = 1;
FOR(i, 1, len) pw[i] = pw[i - 1] * B;
FOR(i, 1, len) ps[i] = ps[i - 1] + pw[i];
vector<SgT> T(n + 1, SgT(len));
FOR(i, 1, n) {
string s;
cin >> s; s = ' ' + s;
FOR(j, 1, len) a[j] = s[j] - 'a' + 1;
T[i].build(1, 1, len);
S.insert(T[i].query(1, 1, len));
}
REP(_, m) {
int l, r, x;
char c;
cin >> x >> l >> r >> c;
int v = c - 'a' + 1;
S.erase(T[x].query(1, 1, len));
T[x].modify(1, l, r, v);
S.insert(T[x].query(1, 1, len));
cout << SZ(S) << endl;
}
}
SHA256: 3ef39dd2
现在在一棵节点数为 \(n\) 的树上,根节点为 \(1\)。
定义节点 \(a\) 在节点 \(b\) 的北方当且仅当 \(a\) 是 \(b\) 的祖先。
可以从一个节点瞬间移动到另一个点上,定义一次瞬间移动是向北移动当且仅当从原来的点移动到了它北方的节点。
你需要回答独立的 \(q\) 次询问,第 \(i\) 次询问如下:
现在要在子树 \(x_i\) 里进行旅游。请求出从任意子树内节点出发,遍历子树内的每个节点恰好一次,并且向北移动的次数恰好为 \(y_i\) 的方案数对 \(10^9+7\) 取余后的结果。
直接按照题目设置状态比较难转移。
考虑 \(f_{i, j}\) 为 \(i\) 子树至少向北移动了 \(j\) 次。
这样可以每次用 \(O(n)\) 二项式反演求出答案,和 \(O(n^2)\) 的预处理。
两个字树的合并的方案为 $\binom{sz_a+sz_b-i-j}{sz_a-i} $。
const int N = 5e3 + 5;
const int P = 1e9 + 7;
int C[N][N];
int n, q;
vector<int> e[N];
int f[N][N], g[N], sz[N];
void dfs(int u, int fa) {
f[u][0] = 1;
for(int v : e[u]) {
if(v == fa) {
continue;
}
dfs(v, u);
memset(g, 0, sizeof(g));
FOR(i, 0, sz[u]) {
FOR(j, 0, sz[v] - 1) {
int val = (1ll * f[u][i] * f[v][j]) % P;
val = (1ll * val * C[sz[u] - i + sz[v] - j][sz[u] - i]) % P;
g[i + j] = (g[i + j] + val) % P;
}
}
sz[u] += sz[v];
memcpy(f[u], g, sizeof(g));
}
ROF(i, sz[u] + 1, 0) {
int val = f[u][i];
if(i) val = (val + f[u][i - 1]) % P;
val = (1ll * val * (sz[u] + 1 - i)) % P;
f[u][i] = val;
}
sz[u]++;
}
void solve() {
cin >> n >> q;
REP(_, n - 1) {
int u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
REP(i, N) {
C[i][0] = 1;
FOR(j, 1, i) {
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
}
}
dfs(1, 0);
REP(_, q) {
int x, y;
cin >> x >> y;
int res = 0;
FOR(j, y, sz[x]) {
int val = (1ll * f[x][j] * C[j][y]) % P;
if((j - y) % 2) res = (res - val + P) % P;
else res = (res + val) % P;
}
cout << res << endl;
}
}
SHA256: b60171a0
给定一个 \(01\) 矩阵 \(a\)。
\(a_{x,y}=1\) 则 \((x, y)\) 有点。
求有多少个大小为 \(4\) 的点集,满足点集中的点刚好为一个正方形的四个顶点。
\(n \le 500\)
发现 \(O(n^3)\) 不好做,直接 bitset 压位,
\(O(\frac{n^4}{w})\) 可以通过。
const int N = 5e2 + 5;
int n, t, a[N][N];
bitset<N> b[N], c[N], d[N], e[N];
int vis[N];
string s;
void solve() {
cin >> n >> t;
if(t == 5) {
cout << 0 << endl;
return;
}
FOR(i, 1, n) {
cin >> s; s = ' ' + s;
FOR(j, 1, n) {
a[i][j] = (s[j] == '1');
b[i][j] = a[i][j];
}
}
ll ans = 0;
FOR(j, 1, n - 1) {
FOR(k, 1, n) {
d[k] = b[k] >> j;
}
FOR(i, 0, n - 1) {
FOR(k, 1, n) {
int u = k - i;
vis[k] = 1;
if(u > 0) c[k] = b[k] & d[u];
else vis[k] = 0;
}
FOR(k, 1, n) {
int u = k - j;
if(u > 0 && vis[u]) ans += (c[k] & (c[u] << i)).count();
}
}
}
cout << ans << endl;
}