2023ZROI省选十连测

胡测6 / ZROI2023省选十连测Day3

A. 数正方体

可以发现是求

a=1Ab=1Bc=1C[ab>=c][ac>=b][bc>=a]

直接搞不好整,当然您如果和牛神一样巨也是能做的。

正难则反,考虑减去不合法的方案

a=1Ab=1Bc=1C[ab<c]+[ac<b]+[bc<a]

发现三种情况无交,可以分开计算

a=1Ab=1Bc=1C[ab<c]

=a=1Ab=1Bmax(Cab,0)

=a=1Ab=1min(B,C/a)Cab

拆开算算整除分块一下即可。

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. 数树上点

fx,i 表示 x 子树内,距离 x 最近的点的距离为 i 的方案数

长链剖分+后缀和优化做到 O(n)

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. 数区间集

如果两个相同区间有交,那么区间端点旁边一定存在区间内的元素

我们定义极大的区间为端点两侧相邻位置不存在区间内的元素

可以发现这样去掉了相交的相同区间,和相邻的相同区间,并且相同的区间有被统计上的。

现在就是要减去不相邻但相同的区间,可以发现两个区间都没有重复的数,

记这两个区间为 [l1,r1][l2,r2], 记ci 表示与 ai 相同的前一个数的位置

那么可以得到 maxi=l2r2cimini=l2r2ci=rl

条件是充要的。

于是线段树套路,枚举l2 查询合法的 r2,单调栈维护最大最小值,线段树维护最小值,及个数

需要注意的是不能重复减去相邻的区间,那么考虑 l21 的位置,与之相同的另外一个数如果在右边,那么合法的右端点一定在那个位置左边,改变一下查询范围即可

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. 命题

考虑从低位到高位处理,如果这一位是存在,那么把两边的结果或运算,否则是与运算

向上合并即可

写出来像极了 fwt

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. 分组

钦定 k 个人进了 A 组,因为 A,B 本身没有不同,所以答案乘 2 即可

考虑在某个位置,他填满了一个组,对于后面的所有人无论如何都会去另外一个组,枚举这个位置进行计数

考虑在 (pi,pi+1) 之间的 j ,因为后面还有必须到 A 的人,所以最后一个人必然去了 B

那么方案数为 (ji1n1)2j

改枚举 j 为枚举 ji, 预处理前缀和可以快速计算

考虑在 pk 位置

方案数为 (pkknk)2pk

考虑在后面

如果 A 先结束,方案为 (jk1nk1)2j

如果 B 先结束,方案为 (jk1n1)2j

固定 k, 可以预处理前缀和快速计算

而不同的 k 最多根号级别,于是分别处理即可

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. 题目

求每个位置的最大值,那么就是求有多少位置一定比他大

考虑位置 p

首先对于 i<p 并且 ai>=ap 那么显然原来的数 cci>cp

现在考虑后面有哪些数比 cp

考虑一个数 ai, 他一定接在某个 aj=ai1(j<i) 后,称 i 依赖 j

那么显然依赖关系是可以传递的

那么对于 i>p 如果 i 依赖 j 满足 j<p 并且 aj>=ap 那么 ip

由于依赖在位置上单调,于是可以归约成统计 i 依赖 j 满足 aj=ap j<pi 的数量

由于依赖的位置可能有多个,为了满足 i 最少的限制,每个数直接依赖离他最近的数显然是最优的

按照数值从大到小考虑,每次向他依赖的数转移,值相同的做一次前缀和即可得到答案

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摆烂

考虑 nmh<=1e5 那么每个位置可以用一个点代替

有物品的点看做源点,那么每次询问就是查询多源最短路

直接做复杂度不对,对操作分块,每 q 次重新处理最短路

同一块内的暴力查询

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的树

首先建立 AC 自动机,然后发现可以用矩阵进行 DP 转移

那么用线段树维护一下

此时复杂度还是不对

但是考虑询问是一个向量,向量乘矩阵是 n2

于是改一下乘法即可

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

长剖+ ntt

合并多条链后只剩下最短的长度,可以预先推一遍

一条链每次都乘上 (1+x) 显然复杂度不对

懒惰标记一下,在需要合并时 ntt 计算,即可

代码不想写了。

posted @   Chen_jr  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示