2023ZROI省选十连测
胡测6 / ZROI2023省选十连测Day3
A. 数正方体
可以发现是求
直接搞不好整,当然您如果和牛神一样巨也是能做的。
正难则反,考虑减去不合法的方案
发现三种情况无交,可以分开计算
拆开算算整除分块一下即可。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
ll read(){
ll x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int mod = 998244353, inv2 = 499122177;
ll calc(ll l, ll r){return 1ll * ((l + r) % mod) * ((r - l + 1) % mod) % mod * inv2 % mod;}
ll calc(ll a, ll b, ll c){
ll ans = 0;
for(ll l = 1, r, x; l <= a && l <= c; l = r + 1){
r = c / (c / l); x = c / l; r = min(r, a);
if(x >= b){
ans = (ans + 1ll * ((r - l + 1) % mod) * (b % mod) % mod * (c % mod) % mod - 1ll * calc(1, b) * calc(l, r)) % mod;
}else{
ans = (ans + 1ll * ((r - l + 1) % mod) * (x % mod) % mod * (c % mod) % mod - 1ll * calc(l, r) * calc(1, x)) % mod;
}
}
return (ans % mod + mod) % mod;
}
void solve(){
ll a = read(), b = read(), c = read();
ll ans = (a % mod) * (b % mod) % mod * (c % mod) % mod;
ans = (ans - calc(a, b, c)) % mod;
ans = (ans - calc(a, c, b)) % mod;
ans = (ans - calc(b, c, a)) % mod;
ans = (ans % mod + mod) % mod;
printf("%lld\n",ans);
}
int main(){
freopen("cube.in","r",stdin);
freopen("cube.out","w",stdout);
int t = read(); for(int i = 1; i <= t; ++i)solve();
return 0;
}
B. 数树上点
表示 子树内,距离 最近的点的距离为 的方案数
长链剖分+后缀和优化做到
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
ll read(){
ll x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 2e6 + 55, mod = 998244353;
int n, d, tot, head[maxn], fa[maxn], son[maxn], mxd[maxn], h[maxn], lp, ans, tmp[maxn], s[maxn], f[maxn];
struct edge{int to, net;}e[maxn << 1 | 1];
void link(int u, int v){
e[++tot] = {v, head[u]};
head[u] = tot;
}
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
void fp(int x){h[x] = lp + 1; lp += mxd[x];}
void dfs1(int x){
mxd[x] = 1;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v != fa[x]){
fa[v] = x; dfs1(v);
if(mxd[v] >= mxd[x]){
if(son[x])fp(son[x]);
son[x] = v; mxd[x] = mxd[v] + 1;
}else fp(v);
}
}
}
void solve(int x){
f[h[x]] = 1;
if(son[x]){
h[son[x]] = h[x] + 1; solve(son[x]);
s[h[x]] = s[h[son[x]]];
if(mxd[x] >= d + 1)add(f[h[x]], s[h[x] + d]);
}
add(s[h[x]], f[h[x]]);
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v != fa[x] && v != son[x]){
solve(v);
for(int i = 0; i <= mxd[v] + 5; ++i)tmp[i] = 0;
for(int i = 0; i < mxd[v]; ++i){
int l = max(i + 1, d - i - 1);
if(l < mxd[x])add(tmp[i + 1], 1ll * f[h[v] + i] * s[h[x] + l] % mod);
l = max(i + 1, d - i);
if(l > 0 && l <= mxd[v])add(tmp[i], 1ll * s[h[v] + l - 1] * f[h[x] + i] % mod);
}
for(int i = 0; i <= mxd[v]; ++i){
add(f[h[x] + i], tmp[i]);
if(i)add(f[h[x] + i], f[h[v] + i - 1]);
}
for(int i = mxd[v]; i >= 0; --i){
if(i + 1 == mxd[x])s[h[x] + i] = f[h[x] + i];
else s[h[x] + i] = (s[h[x] + i + 1] + f[h[x] + i]) % mod;
}
}
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n = read(), d = read();
for(int i = 1, u, v; i < n; ++i){
u = read(); v = read();
link(u, v); link(v, u);
}
dfs1(1); fp(1); solve(1);
printf("%d\n",(s[h[1]] + 1) % mod);
return 0;
}
C. 数区间集
如果两个相同区间有交,那么区间端点旁边一定存在区间内的元素
我们定义极大的区间为端点两侧相邻位置不存在区间内的元素
可以发现这样去掉了相交的相同区间,和相邻的相同区间,并且相同的区间有被统计上的。
现在就是要减去不相邻但相同的区间,可以发现两个区间都没有重复的数,
记这两个区间为 , 记 表示与 相同的前一个数的位置
那么可以得到
条件是充要的。
于是线段树套路,枚举 查询合法的 ,单调栈维护最大最小值,线段树维护最小值,及个数
需要注意的是不能重复减去相邻的区间,那么考虑 的位置,与之相同的另外一个数如果在右边,那么合法的右端点一定在那个位置左边,改变一下查询范围即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 5e5 + 55;
int n, a[maxn], b[maxn], c[maxn];
ll ans;
struct BIT{
int t[maxn];
int lowbit(int x){return x & -x;}
void add(int x, int val){while(x <= n){t[x] += val; x += lowbit(x);}}
int query(int x){int ans = 0; while(x){ans += t[x]; x -= lowbit(x);} return ans;}
}bt;
struct data{
int val, cnt;
friend data operator + (const data &x, const data &y){
if(x.val < y.val)return x;
if(x.val > y.val)return y;
return data{x.val, x.cnt + y.cnt};
}
};
struct seg{
struct node{ int tag; data val; }t[maxn << 2 | 1];
void upd(int x, int val){t[x].tag += val; t[x].val.val += val;}
void push_up(int x){t[x].val = t[x << 1].val + t[x << 1 | 1].val;}
void push_down(int x){if(t[x].tag){upd(x << 1, t[x].tag), upd(x << 1 | 1, t[x].tag), t[x].tag = 0;}}
void built(int x, int l, int r){
t[x].val.cnt = r - l + 1;
if(l == r)return;
int mid = (l + r) >> 1;
built(x << 1, l, mid);
built(x << 1 | 1, mid + 1, r);
}
void modify(int x, int l, int r, int L, int R, int val){
if(L <= l && r <= R)return upd(x, val);
push_down(x); int mid = (l + r) >> 1;
if(L <= mid)modify(x << 1, l, mid, L, R, val);
if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
push_up(x);
}
data query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x].val;
push_down(x); int mid = (l + r) >> 1;
if(R <= mid)return query(x << 1, l, mid, L, R);
if(L > mid)return query(x << 1 | 1, mid + 1, r, L, R);
return query(x << 1, l, mid, L, R) + query(x << 1 | 1, mid + 1, r, L, R);
}
}T;
int st1[maxn], top1, st2[maxn], top2;
int main(){
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i)a[i] = read();
for(int i = 1; i <= n; ++i){
if(c[a[i]]){
b[i] = c[a[i]];
b[b[i]] = i;
}else c[a[i]] = i;
}
for(int l = n; l >= 1; --l){
bt.add(l, 1);
if(b[l] > l)bt.add(b[l], -1);
if(b[l - 1] >= l)ans += bt.query(b[l - 1] - 1);
else ans += bt.query(n);
}
for(int i = 1; i <= n; ++i)if(b[i] < i)c[i] = b[i]; else c[i] = 0;
st1[0] = st2[0] = n + 1;
T.built(1, 1, n);
for(int l = n, r = n; l >= 1; --l){
T.modify(1, 1, n, l, l, -l);
while(top1 && c[st1[top1]] > c[l]){
T.modify(1, 1, n, st1[top1], st1[top1 - 1] - 1, c[st1[top1]] - c[l]);
--top1;
}
st1[++top1] = l;
while(top2 && c[st2[top2]] < c[l]){
T.modify(1, 1, n, st2[top2], st2[top2 - 1] - 1, -c[st2[top2]] + c[l]);
--top2;
}
st2[++top2] = l;
if(c[l]){
if(b[l - 1] >= l)r = min(r, b[l - 1] - 1);
if(l <= r){
data res = T.query(1, 1, n, l, r);
if(res.val == -l)ans -= res.cnt;
}
}else r = l - 1;
}
printf("%lld\n",ans);
return 0;
}
胡测7 / ZROI2023省选十连测Day4
A. 命题
考虑从低位到高位处理,如果这一位是存在,那么把两边的结果或运算,否则是与运算
向上合并即可
写出来像极了
code
#include<bits/stdc++.h>
using namespace std;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = (1 << 20) + 55;
int n, m, q, f[maxn];
char s[maxn];
int main(){
freopen("proposition.in","r",stdin);
freopen("proposition.out","w",stdout);
n = read(); scanf("%s",s);
m = 1 << n;
for(int i = 0; i < m; ++i)f[i] = (s[i] == '1');
for(int l = 2, hl = 1; l <= m; hl = l, l <<= 1)
for(int i = 0; i < m; i += l)
for(int j = i; j < i + hl; ++j){
bool x = f[j], y = f[j + hl];
f[j] = x & y; f[j + hl] = x | y;
}
q = read();
for(int i = 1; i <= q; ++i)printf("%d",f[read()]); printf("\n");
return 0;
}
B. 分组
钦定 个人进了 组,因为 本身没有不同,所以答案乘 即可
考虑在某个位置,他填满了一个组,对于后面的所有人无论如何都会去另外一个组,枚举这个位置进行计数
考虑在 之间的 ,因为后面还有必须到 的人,所以最后一个人必然去了
那么方案数为
改枚举 为枚举 , 预处理前缀和可以快速计算
考虑在 位置
方案数为
考虑在后面
如果 先结束,方案为
如果 先结束,方案为
固定 , 可以预处理前缀和快速计算
而不同的 最多根号级别,于是分别处理即可
code
#include<bits/stdc++.h>
using namespace std;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 2e5 + 55, mod = 998244353;
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
int n, q, fac[maxn], ifac[maxn], pi2[maxn], s[maxn], s1[maxn], s2[maxn], ans[maxn];
int c(int n, int m){if(n < m || n < 0 || m < 0)return 0;return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
struct node{
vector<int>p; int id;
friend bool operator < (const node &x, const node &y){return x.p.size() < y.p.size();}
void solve(){
int k = p.size() - 1;
int res = 0;
for(int i = 0; i < k; ++i){
int r = p[i + 1] - 1 - i, l = max(1, p[i] + 1 - i);
if(l <= r)res = (res + 1ll * (s[r] - s[l - 1] + mod) % mod * pi2[i]) % mod;
}
if(p[k] < n + n){
res = (res + 1ll * c(p[k] - k, n - k) * pi2[p[k]]) % mod;
int l = max(p[k] + 1 - k, 1), r = n + n - 1 - k;
res = (res + 1ll * (s1[r] - s1[l - 1] + mod) % mod * pi2[k]) % mod;
res = (res + 1ll * (s2[r] - s2[l - 1] + mod) % mod * pi2[k]) % mod;
}
res = 2ll * res % mod;
ans[id] = res;
}
}d[maxn];
void get_sum(int k){
for(int i = max(1, n - k); i <= n + n; ++i)s1[i] = 1ll * c(i - 1, n - k - 1) * pi2[i] % mod;
for(int i = max(1, n - k); i <= n + n; ++i)s1[i] = (s1[i] + s1[i - 1]) % mod;
for(int i = n; i <= n + n; ++i)s2[i] = 1ll * c(i - 1, n - 1) * pi2[i] % mod;
for(int i = n; i <= n + n; ++i)s2[i] = (s2[i] + s2[i - 1]) % mod;
}
int main(){
freopen("group.in","r",stdin);
freopen("group.out","w",stdout);
n = read(), q = read();
fac[0] = ifac[0] = pi2[0] = 1; for(int i = 1; i <= n + n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[n + n] = qpow(fac[n + n], mod - 2); for(int i = n + n - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
pi2[1] = (mod + 1) >> 1; for(int i = 1; i <= n + n; ++i)pi2[i] = 1ll * pi2[i - 1] * pi2[1] % mod;
for(int i = n; i <= n + n; ++i)s[i] = 1ll * c(i - 1, n - 1) * pi2[i] % mod;
for(int i = n; i <= n + n; ++i)s[i] = (s[i] + s[i - 1]) % mod;
for(int i = 1; i <= q; ++i){
int k = read(); d[i].p.push_back(0); d[i].id = i;
for(int j = 1; j <= k; ++j)d[i].p.push_back(read());
}
sort(d + 1, d + q + 1);
for(int i = 1; i <= q; ++i){
if(d[i].p.size() != d[i - 1].p.size())get_sum(d[i].p.size() - 1);
d[i].solve();
}
for(int i = 1; i <= q; ++i)printf("%d\n",ans[i]);
return 0;
}
C. 题目
求每个位置的最大值,那么就是求有多少位置一定比他大
考虑位置
首先对于 并且 那么显然原来的数 有
现在考虑后面有哪些数比 大
考虑一个数 , 他一定接在某个 后,称 依赖
那么显然依赖关系是可以传递的
那么对于 如果 依赖 满足 并且 那么 比 大
由于依赖在位置上单调,于是可以归约成统计 依赖 满足 的 的数量
由于依赖的位置可能有多个,为了满足 最少的限制,每个数直接依赖离他最近的数显然是最优的
按照数值从大到小考虑,每次向他依赖的数转移,值相同的做一次前缀和即可得到答案
code
#include<bits/stdc++.h>
using namespace std;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e7 + 5;
int __my_rand(int *seed){
*seed = *seed * 1103515245 + 12345;
return ((unsigned)*seed) / 34;
}
void gen(int N, int Lim, int seed, int* a){
int cur = 0;
for (int i = 1; i <= N; i ++) {
int rd = __my_rand(&seed);
if (rd % std::min(10, cur + 1) == 0 && cur < Lim) a[i] = ++cur;
else a[i] = (__my_rand(&seed) % cur) + 1;
}
}
const int mod = 998244353;
int n, lim, seed, a[maxn], p[maxn], rem[maxn], mx, c[maxn];
vector<int>pos[maxn];
int main(){
// freopen("topic.in","r",stdin);
// freopen("topic.out","w",stdout);
n = read(), lim = read(), seed = read();
gen(n, lim, seed, a);
for(int i = 1; i <= n; ++i){
p[i] = rem[a[i] - 1];
rem[a[i]] = i; mx = max(mx, a[i]);
pos[a[i]].push_back(i);
}
for(int val = mx; val >= 1; --val){
for(int v : pos[val])c[p[v]] += c[v] + 1;
for(int i = 1; i < pos[val].size(); ++i)c[pos[val][i]] += c[pos[val][i - 1]] + 1;
}
for(int i = 1; i <= n; ++i)c[i] = n - c[i];
int ans = 0;
for(int i = n; i >= 1; --i)ans = (233ll * ans + c[i]) % mod;
printf("%d\n",ans);
return 0;
}
胡测8 / ZROI2023省选十连测Day8
A. 天使玩偶/SJY摆烂
考虑 那么每个位置可以用一个点代替
有物品的点看做源点,那么每次询问就是查询多源最短路
直接做复杂度不对,对操作分块,每 次重新处理最短路
同一块内的暴力查询
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e5 + 55, inf = 0x3f3f3f3f;
int n, m, h, Q, tot, s, B, dis[maxn], head[maxn];
struct edge{int to, net;}e[maxn * 7];
void add(int u, int v){
e[++tot] = {v, head[u]};
head[u] = tot;
}
int id(int x, int y, int z){return (x - 1) * m * h + (y - 1) * h + z;}
queue<int>q;
struct node{int x, y, z;};
vector<node>rem;
int query(){
int x = read(), y = read(), z = read();
int ans = dis[id(x, y, z)];
for(node v : rem)ans = min(ans, abs(x - v.x) + abs(y - v.y) + abs(z - v.z));
return ans;
}
void clear(){
for(node v : rem)add(s, id(v.x, v.y, v.z));
rem.clear(); q.push(s);
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(dis[v] > dis[x] + 1){
dis[v] = dis[x] + 1;
q.push(v);
}
}
}
}
int main(){
freopen("sjy.in","r",stdin);
freopen("sjy.out","w",stdout);
n = read(), m = read(), h = read(), Q = read();
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
for(int k = 1; k <= h; ++k){
if(i > 1)add(id(i, j, k), id(i - 1, j, k));
if(j > 1)add(id(i, j, k), id(i, j - 1, k));
if(k > 1)add(id(i, j, k), id(i, j, k - 1));
if(i < n)add(id(i, j, k), id(i + 1, j, k));
if(j < m)add(id(i, j, k), id(i, j + 1, k));
if(k < h)add(id(i, j, k), id(i, j, k + 1));
}
s = n * m * h + 1; B = sqrt(Q);
for(int i = 1; i <= n * m * h; ++i)dis[i] = inf;
dis[s] = -1;
for(int i = 1; i <= Q; ++i){
int op = read();
if(op & 1){
node tmp; tmp.x = read(); tmp.y = read(); tmp.z = read();
rem.push_back(tmp);
}else printf("%d\n",query());
if(i % B == 0)clear();
}
return 0;
}
B. Koishi的树
首先建立 自动机,然后发现可以用矩阵进行 转移
那么用线段树维护一下
此时复杂度还是不对
但是考虑询问是一个向量,向量乘矩阵是 的
于是改一下乘法即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 10005, mod = 998244353;
int cnt;
struct matrix{
int a[36][36];
matrix(){memset(a, 0, sizeof(a));}
void clear(){memset(a, 0, sizeof(a));}
friend matrix operator * (const matrix &x, const matrix &y){
matrix res;
for(int i = 0; i < cnt; ++i)
for(int k = 0; k < cnt; ++k)
for(int j = 0; j < cnt; ++j)
res.a[i][j] = (res.a[i][j] + 1ll * x.a[i][k] * y.a[k][j]) % mod;
return res;
}
friend matrix operator + (const matrix &x, const matrix &y){
matrix res;
for(int i = 0; i < cnt; ++i)
for(int j = 0; j < cnt; ++j)
res.a[i][j] = (x.a[i][j] + y.a[i][j]) % mod;
return res;
}
void print(){
for(int i = 0; i < cnt; ++i, printf("\n"))
for(int j = 0; j < cnt; ++j)
printf("%d ",a[i][j]);
printf("\n");
}
}base[26], val[maxn];
char s[maxn][30], c[40];
int n, m, q, head[maxn], tot;
struct edge{int to, net;}e[maxn * 2];
void add(int u, int v){
e[++tot] = {v, head[u]};
head[u] = tot;
}
struct AC{
int ch[40][26], root = 1, cnt = 1, fail[40];
bool flag[40];
void ins(int len){
int now = root;
for(int i = 1; i <= len; ++i){
if(!ch[now][c[i] - 'a'])ch[now][c[i] - 'a'] = ++cnt;
now = ch[now][c[i] - 'a'];
}
flag[now] = true;
}
queue<int>q;
void build(){
for(int i = 0; i < 26; ++i)
if(ch[1][i])q.push(ch[1][i]), fail[ch[1][i]] = root;
else ch[1][i] = root;
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = 0; i < 26; ++i)
if(ch[x][i])q.push(ch[x][i]), fail[ch[x][i]] = ch[fail[x]][i];
else ch[x][i] = ch[fail[x]][i];
}
for(int c = 0; c < 26; ++c){
for(int i = 1; i <= cnt; ++i){
if(flag[i])++base[c].a[i - 1][i - 1];
else ++base[c].a[i - 1][ch[i][c] - 1];
}
}
}
}A;
int fa[maxn], son[maxn], si[maxn], dep[maxn], top[maxn], dfn[maxn], id[maxn], tim, md[maxn];
void dfs1(int x){
si[x] = 1;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa[x])continue;
dep[v] = dep[x] + 1; fa[v] = x; md[v] = (i + 1) / 2;
dfs1(v); si[x] += si[v];
if(si[v] > si[son[x]])son[x] = v;
}
}
void dfs2(int x, int tp){
id[dfn[x] = ++tim] = x; top[x] = tp;
if(son[x])dfs2(son[x], tp);
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v != fa[x] && v != son[x])dfs2(v, v);
}
}
int ans[40], tmp[40];
void mul(matrix &x){
for(int i = 0; i < cnt; ++i)
for(int j = 0; j < cnt; ++j)
tmp[j] = (tmp[j] + 1ll * ans[i] * x.a[i][j]) % mod;
for(int i = 0; i < cnt; ++i)ans[i] = tmp[i], tmp[i] = 0;
}
struct seg{
matrix t1[maxn << 2 | 1], t2[maxn << 2 | 1];
void push_up(int x){
t1[x] = t1[x << 1] * t1[x << 1 | 1];
t2[x] = t2[x << 1 | 1] * t2[x << 1];
}
void build(int x, int l, int r){
if(l == r){
t1[x] = t2[x] = val[md[id[l]]];
return;
}
int mid = (l + r) >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
push_up(x);
}
void query1(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return mul(t1[x]);
int mid = (l + r) >> 1;
if(L <= mid)query1(x << 1, l, mid, L, R);
if(R > mid)query1(x << 1 | 1, mid + 1, r, L, R);
}
void query2(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return mul(t2[x]);
int mid = (l + r) >> 1;
if(R > mid)query2(x << 1 | 1, mid + 1, r, L, R);
if(L <= mid)query2(x << 1, l, mid, L, R);
}
}T;
int LCA(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] > dep[top[v]])u = fa[top[u]];
else v = fa[top[v]];
}
return dep[u] < dep[v] ? u : v;
}
int query(int u, int v){
for(int i = 0; i < cnt; ++i)ans[i] = i == 0;
int lca = LCA(u, v);
while(dep[top[u]] > dep[lca]){
T.query2(1, 1, n, dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
if(dep[lca] < dep[u])T.query2(1, 1, n, dfn[lca] + 1, dfn[u]);
vector<pii>rem;
while(dep[top[v]] > dep[lca]){
rem.push_back(pii(dfn[top[v]], dfn[v]));
v = fa[top[v]];
}
if(dep[lca] < dep[v])T.query1(1, 1, n, dfn[lca] + 1, dfn[v]);
while(rem.size()){
pii x = rem.back(); rem.pop_back();
T.query1(1, 1, n, x.first, x.second);
}
int res = 0;
for(int i = 0; i < cnt; ++i)if(A.flag[i + 1])res = (res + ans[i]) % mod;
return res;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n = read(), m = read(), q = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
add(u, v); add(v, u);
scanf("%s",s[i] + 1);
}
for(int i = 1; i <= m; ++i){
scanf("%s",c + 1);
A.ins(strlen(c + 1));
}
cnt = A.cnt;
A.build();
for(int i = 1; i < n; ++i){
int len = strlen(s[i] + 1);
for(int j = 1; j <= len; ++j)
val[i] = val[i] + base[s[i][j] - 'a'];
}
dfs1(1); dfs2(1, 1);
T.build(1, 1, n);
for(int i = 1; i <= q; ++i){
int u = read(), v = read();
printf("%d\n",query(u, v));
}
return 0;
}
C. APjifengc的Galgame
长剖+
合并多条链后只剩下最短的长度,可以预先推一遍
一条链每次都乘上 显然复杂度不对
懒惰标记一下,在需要合并时 计算,即可
代码不想写了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】