重庆八中集训 day4
20220617
sign
题意
一个 \(1\) 为根的树,每个点有点权 \(a_i\),然后一个方案的时候,你就可以选择子树里节点 \(j\) 的点权 \(b_j \in [0, a_i]\),然后子树 \(u\) 的权值就是 \(son_u^{\sum_{i} bi}\),其中 \(son_u\) 表示 \(u\) 的儿子数。然后求对于每个子树来说,所有方案的权值之和。
题解
我们考虑,不同的 \(son_k\) 最多有 \(O(\sqrt N)\) 个。然后就是你对于一个子树 \(i\),然后记 \(f_{i, j}\) 表示当前 \(son_u = j\) 的权值之和。然后一个节点的贡献明显就是 \(\sum_{i\ge 0} son_u ^ i\) 这个可以等比数列求和快速算。
注意到快速幂的 \(\log A\) 不能要,怎么办捏,我们注意到可令 \(B = \sqrt {A}\),然后对于小于 \(B\) 的幂次预处理,然后大于 \(B\) 的幂次,对于原先的 \(son_u\),我们变成 \(son_u ^ B\) 之后,在预处理 \(B\) 个就可以 \(O(1)\) 查询快速幂。
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
*s = 0;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 1e5 + 10, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) return x - MOD;
else if (x < 0) return x + MOD;
else return x;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac;} e[M_num];
inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
const int MAXM = 10005;
int N, d[MAXN], fa[MAXN], t[MAXN], A[MAXN], f[MAXN][500], ans[MAXN], pw1[500][MAXM], pw2[500][MAXM], INV[MAXN];
Graph<MAXN, MAXN> tr;
inline int qpow(int x, int y) {return (ll) pw2[x][y / MAXM] * pw1[x][y % MAXM] % MOD;}
inline int gets(int x, int y) {
// 1 + t[x] + t[x] ^ 2 + ... + t[x] ^ y
if (t[x] == 1) return y + 1;
return (qpow(x, y + 1) - 1LL) * INV[x] % MOD;
}
void dfs(int u) {
for (int i = 1; i <= *t; ++i) f[u][i] = gets(i, A[u]);
go(tr, u) {
dfs(v);
for (int j = 1; j <= *t; ++j) f[u][j] = (ll) f[u][j] * f[v][j] % MOD;
}
ans[u] = d[u] ? f[u][std::lower_bound(t + 1, t + 1 + *t, d[u]) - t] : 1;
}
int main() {
freopen("sign.in", "r", stdin);
freopen("sign.out", "w", stdout);
qwq::init(20050112);
read(N);
tr.init();
for (int i = 2; i <= N; ++i) read(fa[i]), tr.add_edge(fa[i], i), d[fa[i]]++;
for (int i = 1; i <= N; ++i) if (d[i]) t[++*t] = d[i];
for (int i = 1; i <= N; ++i) read(A[i]);
sort(t + 1, t + 1 + *t);
*t = unique(t + 1, t + 1 + *t) - t - 1;
for (int i = 1; i <= *t; ++i) {
INV[i] = Ksm(t[i] - 1, MOD - 2);
pw1[i][0] = pw2[i][0] = 1;
for (int j = 1; j < MAXM; ++j) pw1[i][j] = (ll) pw1[i][j - 1] * t[i] % MOD;
pw2[i][1] = (ll) pw1[i][MAXM - 1] * t[i] % MOD;
for (int j = 2; j < MAXM; ++j) pw2[i][j] = (ll) pw2[i][j - 1] * pw2[i][1] % MOD;
}
dfs(1);
for (int i = 1; i <= N; ++i) printf("%d\n", ans[i]);
return (0-0);
}
match
题意
两个串 \(s,t\),然后执行下面这个算法,
match(s, t) {
i = 1;
cur = 0;
while (i <= N) {
if (cur < M and s[i] = t[cur + 1]) ++cur;
else cur = 0;
f[i] = cur;
}
}
然后捏,你本来想要得到的 \(f_i\),是最大的 \(l\) 使得 \(s[i - l + 1:i] = t[1:l]\)。
问现在给你一个 \(s\),然后问,有多少 \(t\) 满足上面那个算法是正确的。
题解
首先一个 \(t\) 什么时候是合法的,你首先要找一个最长匹配,肯定是用 \(kmp\) 啊,然后,每次 \(kmp\) 失配的时候都要跳到 \(0\),很神奇。
我们枚举 \(s\) 中的一个子串他是最长的能和 \(T\) 的前缀匹配的节点,匹配长度为 \(l\)。之后,我们对于这段前缀之后的部分,显然可以随便填,然后方案数是 \(5 ^ {m - l - 1}\),然后需要注意的是,\(l+1\) 这个位置的字符应该不能和 \(S\) 更多的匹配,所以要 ban 掉字母,为了不能更长的匹配。
然后做完了,时间复杂度为 \(O(N ^ 3)\)。然后过了。
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
*s = 0;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 2005, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) return x - MOD;
else if (x < 0) return x + MOD;
else return x;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac;} e[M_num];
inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
int N, M, ans, ch[MAXN][5], fail[MAXN], ncnt = 1, tr[MAXN * MAXN][5], pw[MAXN];
char s[MAXN];
void chk(int d) {
bool ban[5];
memset(ban, 0, sizeof ban);
int u = 0;
for (int i = 1; i <= N; ++i) {
if (ch[u][s[i] - 'a'] && ch[u][s[i] - 'a'] != u + 1) return;
if (u == d) ban[s[i] - 'a'] = 1;
u = ch[u][s[i] - 'a'];
}
int cnt = 0;
for (int i = 0; i < 5; ++i) cnt += !ban[i];
if (d == M) ans = Mod(ans + 1);
else ans = (ans + (ll) cnt * pw[M - d - 1]) % MOD;
}
void dfs(int u, int dep, int s) {
if (~s) {
for (int i = 0; i < 5; ++i) ch[dep - 1][i] = ch[fail[dep - 1]][i];
fail[dep] = ch[dep - 1][s];
ch[dep - 1][s] = dep;
for (int i = 0; i < 5; ++i) ch[dep][i] = ch[fail[dep]][i];
}
chk(dep);
if (dep < M) for (int i = 0; i < 5; ++i) if (tr[u][i]) dfs(tr[u][i], dep + 1, i);
}
int main() {
freopen("match.in", "r", stdin);
freopen("match.out", "w", stdout);
pw[0] = 1;
for (int i = 1; i < MAXN; ++i) pw[i] = pw[i - 1] * 5LL % MOD;
qwq::init(20050112);
read(N, M);
reads(s + 1);
for (int i = 1; i <= N; ++i) {
int nw = 1;
for (int j = i; j <= N; ++j) {
if (!tr[nw][s[j] - 'a']) tr[nw][s[j] - 'a'] = ++ncnt;
nw = tr[nw][s[j] - 'a'];
}
}
fail[0] = MAXN - 1;
dfs(1, 0, -1);
ans = (ll) ans * Ksm(pw[M], MOD - 2) % MOD;
printf("%d\n", ans);
// cout << (-3 / 2);
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return (0-0);
}
tree
题意
给你一个 \(N\) 个节点,\(1\) 为根的有根树,然后每条边有边权。求 \(Q\) 次询问,每次询问 \(l,r\),求 \(l \le i \le j \le r\) 的所有点对,有多少个不同的 \(dep(lca(i, j))\)。
题解
首先,考虑对于某个 \(dep\) 求出来它可以贡献的区间,比如说,对于一个点 \(x\),他下面两个点 \((y,z)\) 并且 \(y < z\),于是可以贡献给 \(([1, y], [z, N])\) 这个区间答案,然后一个询问就是 \(([l,r])\) 的单点查询。
发现一个子树里的 \(i\),只要有他的前缀,后缀贡献就好了。
考虑启发式合并即可,维护 set
可以将矩形个数减少为 \(N\log N\),之后用 map<vector<int>>
存一下,然后考虑求一个颜色矩阵的并,我们知道这些矩阵全是 2-side
的,于是只要按照 \((x, y)\) 排序,首先对于一个 \(x\) 保留最小的 \(y\),然后再维护一个单调队列即可。
这就是矩形了。之后就是矩形加法,单点查询,然后离线,扫描线,树状数组。
so easy。
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
*s = 0;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 1e5 + 10, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) return x - MOD;
else if (x < 0) return x + MOD;
else return x;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac, w;} e[M_num];
inline void add_edge(int x, int y, int w) {e[*H] = {y, H[x], w};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
int N, M, ans[MAXN * 5];
ll d[MAXN], t[MAXN];
vector<array<int, 2U>> Orz_jzm[MAXN], opt[MAXN], q[MAXN];
Graph<MAXN, MAXN * 2> tr;
set<int> nd[MAXN];
using std::lower_bound;
void dfs(int u, int lst) {go(tr, u) if (v != lst) d[v] = d[u] + tr.e[i].w, dfs(v, u);}
void dfs2(int u, int lst) {
Orz_jzm[d[u]].push_back({u, u});
nd[u].insert(u);
go(tr, u) if (v != lst) {
dfs2(v, u);
if (nd[v].size() > nd[u].size()) nd[u].swap(nd[v]);
for (auto &i: nd[v]) {
auto it = nd[u].lower_bound(i);
if (it != nd[u].end()) Orz_jzm[d[u]].push_back({i, *it});
if (it != nd[u].begin()) Orz_jzm[d[u]].push_back({*std::prev(it), i});
}
for (auto &i: nd[v]) nd[u].insert(i);
}
}
struct Fenwick_Tree {
int lim, sc[MAXN];
inline void set(int _N) {lim = _N;}
inline void add(int x, int y) {for (; x <= lim; x += x & -x) sc[x] += y;}
inline int qry(int x) {int r = 0; for (; x; x -= x & -x) r += sc[x]; return r;}
} c9;
inline void upd(int x, int y, int z) {opt[z].push_back({x, 1}), opt[z].push_back({y + 1, -1});}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
// std::ios::sync_with_stdio(0);
// cout << std::fixed << std::setprecision(8);
// cin.tie(0);
// cout.tie(0);
qwq::init(20050112);
read(N, M);
tr.init();
for (int i = 1, x, y, w; i < N; ++i) read(x, y, w), tr.add_edge(x, y, w), tr.add_edge(y, x, w);
dfs(1, 0);
for (int i = 1; i <= N; ++i) t[i] = d[i];
sort(t + 1, t + 1 + N);
*t = unique(t + 1, t + 1 + N) - t - 1;
for (int i = 1; i <= N; ++i) d[i] = lower_bound(t + 1, t + 1 + *t, d[i]) - t;//, O(d[i]);
dfs2(1, 0);
for (int i = 1; i <= *t; ++i) {
sort(Orz_jzm[i].begin(), Orz_jzm[i].end());
int lstx = -1;
vector<array<int, 2U>> jzm_ak;
for (auto &j: Orz_jzm[i]) if (j[0] != lstx) lstx = j[0], jzm_ak.push_back(j);
vector<array<int, 2U>> sorry_to_xyx;
for (auto &j: jzm_ak) {
while (sorry_to_xyx.size() && sorry_to_xyx.back()[1] >= j[1]) sorry_to_xyx.pop_back();
sorry_to_xyx.push_back(j);
}
int lst = 0;
for (auto &j: sorry_to_xyx) upd(lst + 1, j[0], j[1]), lst = j[0];
}
for (int i = 1, l, r; i <= M; ++i) read(l, r), q[r].push_back({l, i});
c9.set(N);
for (int i = 1; i <= N; ++i) {
for (auto &j: opt[i]) c9.add(j[0], j[1]);
for (auto &j: q[i]) ans[j[1]] = c9.qry(j[0]);
}
for (int i = 1; i <= M; ++i) printf("%d\n", ans[i]);
// cout << (-3 / 2);
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return (0-0);
}
丁真追妹子
顶针哥哥有 \(N\) 个妹子,第 \(i\) 个妹子可以接受在连续 \(b_i\) 天和丁真哥哥聊天,否则她会把理塘会0000拉黑。
丁真哥哥要在接下来的 \(M\) 天,每天找 \(k_i\) 个妹子聊天,他想被最少的妹子拉黑。
请你给出丁真哥哥最后最多还剩下多少个妹子没有拉黑他。
- \(b\) 固定 \(k\) 不固定 判定是否保存所有妹子
- \(k\) 固定 \(b\) 不固定 判定是否保存所有妹子