2023 省选联测41 - 43

2023 省选联测41

A. 冤家路窄

找出 \(Dag\) 用总路径数减去相遇的路径数

code

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pli;

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 = 1e9 + 7;
int n, m, S, T, head[maxn], tot;
struct edge{int to, net, val;}e[maxn << 1 | 1];
void add(int u, int v, int w){
	e[++tot] = {v, head[u], w};
	head[u] = tot;
}
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
ll diss[maxn], dist[maxn]; bool vis[maxn];
priority_queue<pli, vector<pli>, greater<pli>>Q;
vector<int>g1[maxn], g2[maxn];
int f[maxn][2], deg1[maxn], deg2[maxn];
queue<int>q;
int main(){
	freopen("avoid.in","r",stdin);
	freopen("avoid.out","w",stdout);
	n = read(), m = read(); S = read(); T = read();
	for(int i = 1; i <= m; ++i)	{
		int u = read(), v = read(), w = read();
		add(u, v, w); add(v, u, w);
	}
	memset(diss, 0x3f, sizeof(diss)); 
	diss[S] = 0; Q.push(pli(0, S));
	while(!Q.empty()){
		int x = Q.top().second; Q.pop();
		if(vis[x])continue; vis[x] = true;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			if(diss[v] > diss[x] + e[i].val){
				diss[v] = diss[x] + e[i].val;
				Q.push(pli(diss[v], v));
			}
		}
	}
	memset(dist, 0x3f, sizeof(dist)); 
	memset(vis, 0, sizeof(vis));
	dist[T] = 0; Q.push(pli(0, T));
	while(!Q.empty()){
		int x = Q.top().second; Q.pop();
		if(vis[x])continue; vis[x] = true;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			if(dist[v] > dist[x] + e[i].val){
				dist[v] = dist[x] + e[i].val;
				Q.push(pli(dist[v], v));
			}
		}
	}
	for(int i = 1; i <= tot; i += 2){
		int u = e[i].to, v = e[i + 1].to;
		if(diss[u] > diss[v])swap(u, v);
		if(diss[u] + dist[v] + e[i].val == diss[T]){
			g1[u].push_back(v); ++deg1[v];
			g2[v].push_back(u); ++deg2[u];
		}
	}
	f[S][0] = 1; q.push(S);
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int v : g1[x]){
			--deg1[v]; if(deg1[v] == 0)q.push(v);
			add(f[v][0], f[x][0]);
		}
	}
	f[T][1] = 1; q.push(T);
	int ans = 1ll * f[T][0] * f[T][0] % mod;
	while(!q.empty()){
		int x = q.front(); q.pop();
		if(diss[x] == dist[x])add(ans, mod - 1ll * f[x][1] * f[x][0] % mod * f[x][1]  % mod * f[x][0] % mod);
		for(int v : g2[x]){
			--deg2[v]; if(deg2[v] == 0)q.push(v);
			if(max(diss[v], dist[x]) < min(diss[x], dist[v]))
				add(ans, mod - 1ll * f[x][1] * f[v][0] % mod * f[x][1]  % mod * f[v][0] % mod);
			add(f[v][1], f[x][1]);
		}
	}
	printf("%d\n",ans);
	return 0;
}

B. 夹克姥爷win了win了

结论是 \(k! + k\) 证明用到 \(HAll\) 定理啥的

\(C_{n}^{k} <= P_{n}^{k - 1}\)

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, base = 10000;
struct big{
	int a[maxn];
	void init(int x){while(x){a[++a[0]] = x % base; x /= base;}}
	void mul(int x){
		int res = 0, tmp;
		for(int i = 1; i <= a[0]; ++i){
			tmp = a[i] * x + res;
			a[i] = tmp % base;
			res = tmp / base;
		}
		while(res){a[++a[0]] = res % base; res /= base;}
	}
	void add(int x){
		int res = x, now = 0;
		while(res){
			++now;
			a[now] += res;
			res = a[now] / base;
			a[now] %= base;
		}
		a[0] = max(a[0], now);
	}
	void print(){
		printf("%d",a[a[0]]);
		for(int i = a[0] - 1; i >= 1; --i)printf("%04d",a[i]);
		printf("\n");
	}
}a;

int main(){
	freopen("win.in","r",stdin);
	freopen("win.out","w",stdout);
	int k; cin >> k;
	if(k == 1){
		printf("-1\n");
		return 0;
	}
	a.init(1);
	for(int i = 2; i <= k; ++i)a.mul(i);
	a.add(k);
	a.print();
	return 0;
}

C. 39与93

\(sort\) 后,每次只需枚举到与 \(b\) 位数相同的部分,复杂度是正确的

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, mod = 1e9 + 7;
int n, s, b[maxn], sum;
int mp[13][maxn * 4], tmp[13][maxn * 4];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
int main(){
	freopen("stone.in","r",stdin);
	freopen("stone.out","w",stdout);
	n = read(), s = read();
	for(int i = 1; i <= n; ++i)b[i] = read(), sum ^= b[i];
	sort(b + 1, b + n + 1);
	mp[0][0] = 1;
	for(int l = 1; l <= n; ++l){
		int up = 1; while(up <= b[l]) up <<= 1; --up;
		for(int i = 0; i < s; ++i){
			for(int j = 0; j <= up; ++j)if(mp[i][j])
				add(tmp[(i + 1) % s][j xor b[l]], mp[i][j]),
				add(tmp[i][j], mp[i][j]);
		}
		for(int i = 0; i < s; ++i){
			for(int j = 0; j <= up; ++j)mp[i][j] = tmp[i][j], tmp[i][j] = 0;
		}
	}
	add(mp[n % s][sum], mod - 1);
    printf("%d\n", mp[0][sum]);
	return 0;
}

D. Zbox的刷题I

把贡献分开算,变成算期望轮数和操作数

gtm巨佬的题解

https://www.luogu.com.cn/blog/663705/xing-xuan-lian-ce-41-post

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 mod = 998244353, maxn = 2e6 + 55;
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 pp[maxn], pq[maxn], f[maxn], fac[maxn], ifac[maxn];
int n, p, q, a, b; 
int c(int n, int m){return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
int main(){
	freopen("exercise.in","r",stdin);
	freopen("exercise.out","w",stdout);
	n = read(), p = read(), q = read(), a = read(), b = read();
	p = 1ll * p * qpow(q, mod - 2) % mod; q = (mod + 1 - p) % mod;
	pp[0] = fac[0] = ifac[0] = 1;
	for(int i = 1; i <= n; ++i)pp[i] = 1ll * pp[i - 1] * p % mod;
	for(int i = 0; i <= n; ++i)pq[i] = qpow((mod + 1 - pp[i]) % mod, mod - 2);
	for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[n] = qpow(fac[n], mod - 2);
	for(int i = n - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	int ans = 0;
	for(int i = 1; i <= n; ++i){
		int res = 1ll * c(n, i) * pq[i] % mod;
		if(i & 1)ans = (ans + res) % mod;
		else ans = (ans - res + mod) % mod;
	}
	ans = 1ll * ans * b % mod;
	ans = (ans + 1ll * a * n % mod * qpow(q, mod - 2) % mod) % mod;
	printf("%d\n",ans);
	return 0;
}

2023 省选联测42

A. 猜数字

直接 \(hash\) ,找个好模数

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int maxn = 4e5 + 55, mod = 1004535809;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10ll + (c ^ 48)) % mod; c = getchar();}while(isdigit(c));
	return x;
}

map<int, int>mp;
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 main(){
	freopen("guess.in","r",stdin);
	freopen("guess.out","w",stdout);
	for(int i = 1; i <= 50000; ++i)mp[qpow(i, i)] = i;
	printf("%d\n",mp[read()]);
	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;

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;

int n, q;
vector<int>g[maxn];
int si[maxn], fa[maxn], dep[maxn], top[maxn], son[maxn];
void dfs1(int x){
	si[x] = 1;
	for(int v : g[x])if(v != fa[x]){
		dep[v] = dep[x] + 1;
		fa[v] = x; dfs1(v);
		si[x] += si[v];
		if(si[son[x]] < si[v])son[x] = v;
	}
}
void dfs2(int x, int tp){
	top[x] = tp;
	if(son[x])dfs2(son[x], tp);
	for(int v : g[x])if(v != fa[x] && v != son[x])dfs2(v, v);
}
int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}
int Dis(int u, int v){return dep[u] + dep[v] - 2 * dep[lca(u, v)];}
struct data{
	int u, v, dis; bool flag;
	data(){}
	data(int a, int b, int c, int d){u = a; v = b; dis = c; flag = d;}
	friend data operator + (const data &x, const data &y){
		if(x.flag)return y; if(y.flag)return x;
		int d1 = Dis(x.u, y.u), d2 = Dis(x.u, y.v), d3 = Dis(x.v, y.u), d4 = Dis(x.v, y.v);
		int mxd = max(max(x.dis, y.dis), max(max(d1, d2), max(d3, d4)));
		if(mxd == x.dis)return x;
		if(mxd == y.dis)return y;
		if(mxd == d1)return data(x.u, y.u, d1, 0);
		if(mxd == d2)return data(x.u, y.v, d2, 0);
		if(mxd == d3)return data(x.v, y.u, d3, 0);
		if(mxd == d4)return data(x.v, y.v, d4, 0);
		assert(0);
	}
	void init(int x){u = v = x; dis = 0; flag = false;}
	void rev(){if(flag)flag = false, dis = 0; else flag = true, dis = -1;}
};
struct seg{
	data t[maxn << 2 | 1];
	void build(int x, int l, int r){
		if(l == r)return t[x].init(l);
		int mid = (l + r) >> 1;
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
		t[x] = t[x << 1] + t[x << 1 | 1];
	}
	void modify(int x, int l, int r, int pos){
		if(l == r)return t[x].rev();
		int mid = (l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos);
		else modify(x << 1 | 1, mid + 1, r, pos);
		t[x] = t[x << 1] + t[x << 1 | 1];
	}
	void query(){printf("%d\n",t[1].dis);}
}T;
char s[5];
int main(){
	freopen("quarrel.in","r",stdin);
	freopen("quarrel.out","w",stdout);
	n = read(), q = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();	
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs1(1); dfs2(1, 1); T.build(1, 1, n);
	for(int i = 1; i <= q; ++i){
		scanf("%s",s);
		if(s[0] == 'C')T.modify(1, 1, n, read());
		else T.query();
	}
	return 0;
}

C. 选数问题 V2

赛时奇奇怪怪的过了。

去掉平方因子

质因子数量小于等于 \(2\) ,在质因子之间(不够两个算上 \(1\))连边

问题变成找无向图最小环

不难发现环的元素必然有 \(<= 1000\) 的,所以只从这些位置出发 \(BFS\)

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 = 1e6 + 66;
int n, a[maxn];
int prime[maxn], cnt, mi[maxn];
bool flag[maxn];
void init(int maxn){
	mi[1] = 1;
	for(int i = 2; i <= maxn; ++i){
		if(!flag[i])prime[++cnt] = i, mi[i] = i;
		for(int j = 1; j <= cnt && i * prime[j] <= maxn; ++j){
			flag[i * prime[j]] = true;
			mi[i * prime[j]] = prime[j];
			if(i % prime[j] == 0)break;
		}
	}
}
int nsq(int x){
	int res = 1;
	while(x != 1){
		int now = mi[x];
		x /= mi[x];
		if(mi[x] == now)x /= mi[x];
		else res = res * now;
	}
	return res;
}
vector<int>g[maxn];
queue<int>q;
int dis[maxn], fa[maxn];
int sol(){
	for(int i = 1; i <= n; ++i)if(a[i] == 1)return 1;
	for(int i = 1; i <= n; ++i){
		int x = mi[a[i]], y = mi[a[i] / mi[a[i]]];
		g[x].push_back(y); g[y].push_back(x);
	}
	int ans = INT_MAX;
	for(int i = 1; i <= 1000; ++i){
		for(int j = 1; j <= 1000000; ++j)dis[j] = INT_MAX;
		dis[i] = 0; q.push(i);
		while(!q.empty()){
			int x = q.front(); q.pop();
			if(dis[x] >= ans)continue;
			for(int v : g[x])if(v != fa[x]){
				if(dis[v] == INT_MAX){
					dis[v] = dis[x] + 1;
					fa[v] = x;
					q.push(v);
				}else ans = min(ans, dis[v] + dis[x] + 1);
			}
		}
	}
	return ans == INT_MAX ? -1 : ans;	
}
int main(){
	freopen("choose.in","r",stdin);
	freopen("choose.out","w",stdout);
	init(1e6); n = read();
	for(int i = 1; i <= n; ++i)a[i] = nsq(read());
	printf("%d\n",sol());
	return 0;
}

D. nnntxdy

发现概率跟剩余人数有关

\(f_{i, s}\) 表示一共扣掉 \(i\) 滴血,当前存活的为 \(s\)

转移考虑这一次能否干死一个,在前面任意选择

最后还需要算总方案

\(g_{i, s}\) 表示在 \(s\) 中一共干掉 \(i\) 滴血,谁都没干死的方案数

暴力能过

正解把后面的变成 \(meet in middle\) 即可

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 = 205, mx = (1 << 15) + 5, mod = 998244353;
int n, m, a[maxn], inv[maxn], c[maxn][maxn];
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 f[maxn][mx], g[mx][maxn], cnt[mx], sum[mx], lg[mx];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n = read(), m = read(); int S = (1 << n) - 1;
	for(int i = 0; i < n; ++i)a[i] = read();
	for(int i = 0; i < m; ++i){
		c[i][0] = 1;
		for(int j = 1; j <= m; ++j)c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
	}
	for(int i = 1; i <= n; ++i)inv[i] = qpow(i, mod - 2);
	for(int i = 2; i <= S; i += i)lg[i] = lg[i >> 1] + 1;
	for(int i = 1; i <= S; ++i)cnt[i] = cnt[i >> 1] + (i & 1);
	for(int i = 1; i <= S; ++i)sum[i] = sum[i - (i & -i)] + a[lg[i & -i]];
	f[0][S] = 1;
	for(int i = 0; i < m; ++i){
		for(int s = 1; s <= S; ++s)if(f[i][s]){
			f[i][s] = 1ll * f[i][s] * inv[cnt[s]] % mod;
			int dt = i + 1 - sum[S ^ s];
			for(int j = 0; j < n; ++j)if(((s >> j) & 1) && a[j] <= dt)
				add(f[i + 1][s ^ (1 << j)], 1ll * f[i][s] * c[dt - 1][a[j] - 1] % mod);
			if(dt <= sum[s] - cnt[s])add(f[i + 1][s], f[i][s]);
		}
	}
	g[0][0] = 1;
	int n1 = n >> 1;
	for(int i = 1; i < (1 << n1); ++i){
		int las = i ^ (i & -i), dt = lg[i & -i];
		for(int j = 0; j < a[dt]; ++j)
			for(int k = 0; k + j <= m; ++k)
				add(g[i][j + k], 1ll * g[las][k] * c[j + k][k] % mod);
	}
	int n2 = n - n1;
	for(int i = 1; i < (1 << n2); ++i){
		int tmp = i << n1;
		int las = tmp ^ (tmp & -tmp), dt = lg[tmp & -tmp];
		for(int j = 0; j < a[dt]; ++j)
			for(int k = 0; k + j <= m; ++k)
				add(g[tmp][j + k], 1ll * g[las][k] * c[j + k][k] % mod);
	}
	int ans = 0;
	for(int i = 0; i <= S; ++i)if(f[m][i]){
		int tmp = 0, s1 = i & ((1 << n1) - 1), s2 = i ^ s1, res = m - sum[i ^ S];
		for(int j = 0; j <= res; ++j)add(tmp, 1ll * g[s1][j] * g[s2][res - j] % mod * c[res][j] % mod);
		add(ans, 1ll * f[m][i] * cnt[S ^ i] % mod * tmp % mod);
	}
	printf("%d\n",ans);
	return 0;
}

2023 省选联测43

真的是信心赛啊

A. 树上的数

每个点只会被删一次,直接搞就行

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 = 5e6 + 55;
int n, m, a, b, head[maxn], tot, res;
bool vis[maxn];
struct edge{int to, net;}e[maxn];
void add(int u, int v){e[++tot] = {v, head[u]}; head[u] = tot;}
queue<int>q;
void del(int s){
	if(vis[s])return; 
	q.push(s); 
	while(!q.empty()){
		int x = q.front(); q.pop();
		vis[x] = true; --res;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to; 
			if(!vis[v])q.push(v);
		}
	}
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	res = n = read(), m = read();
	a = read(), b = read();
	add(1, 2); int fa = 1;
	for(int i = 3; i <= n; ++i){
		fa = ((1ll * fa * a + b) ^ 19760817) % (i - 1) + 1;
		add(fa, i);
	}
	int q = read(), x = read(), y = read(), ans = 0;
	for(int i = 1; i <= m; ++i){
		if(i > 1)q = (((1ll * q * x + y) ^ 19760817) ^ (i << 1)) % (n - 1) + 2;
		del(q); ans ^= res; 
	}
	printf("%d\n",ans);
	return 0;
}

B. 时代的眼泪

考虑答案转换为每个点子树内权值小于他的有多少

可以用 \(BIT\) 维护

然后需要换根\(DP\)

多查询几个值就行

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 = 1e6 + 55;
int n, q, w[maxn], h[maxn], fa[maxn];
vector<int>g[maxn];
ll ans[maxn], res;
int rf[maxn], rin[maxn], rall[maxn];
struct BIT{
	int t[maxn];
	#define lowbit(x) (x & -x)
	void add(int x){while(x <= n){++t[x]; x += lowbit(x);}}
	int query(int x){int ans = 0; while(x){ans += t[x]; x -= lowbit(x);} return ans;}
}T;
void dfs(int x){
	rin[x] -= T.query(w[x] - 1);
	if(fa[x])rf[x] -=  T.query(w[fa[x]] - 1);
	T.add(w[x]);
	for(int v : g[x])if(v != fa[x]){fa[v] = x; dfs(v);}
	rin[x] += T.query(w[x] - 1);
	if(fa[x])rf[x] += T.query(w[fa[x]] - 1);
}
void change(int x){
	for(int v : g[x])if(v != fa[x]){
		ans[v] = ans[x] - rf[v] + rall[v] - rin[v];
		change(v);
	}
}
int main(){
	freopen("tears.in","r",stdin);
	freopen("tears.out","w",stdout);
	n = read(), q = read();
	for(int i = 1; i <= n; ++i)w[i] = h[i] = read();
	sort(h + 1, h + n + 1); 
	for(int i = 1; i <= n; ++i)w[i] = lower_bound(h + 1, h + n + 1, w[i]) - h;
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1);
	for(int i = 1; i <= n; ++i)rall[i] = T.query(w[i] - 1);
	for(int i = 1; i <= n; ++i)ans[1] += rin[i];
	change(1);
	for(int i = 1; i <= q; ++i)printf("%lld\n",ans[read()]);
	return 0;
}

C. 传统艺能

考虑维护\(f_a f_b f_c\)

表示以 \(a / b / c\) 结尾的不同的子序列的数量

每个点的 \(dp\) 值是前面最后一个 \(a / b / c\)\(dp\) 值的和 \(+1\)

就是强制选择当前点,在前面选择一个字母必然越靠后情况越多,于是找最后一个,然后加上只选择自己的一个

发现可以矩阵简单维护

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 mod = 998244353;
const int maxn = 1e5 + 55;
struct matrix{
	int a[4][4];
	matrix(){memset(a, 0, sizeof(a));}
	friend matrix operator * (const matrix &x, const matrix &y){
		matrix res;
		for(int i = 0; i < 4; ++i)
			for(int k = 0; k < 4; ++k)
				for(int j = 0; j < 4; ++j)
					res.a[i][j] = (res.a[i][j] + 1ll * x.a[i][k] * y.a[k][j]) % mod;
		return res;
	}
	void get(int c){
		for(int i = 0; i < 4; ++i)
			for(int j = 0; j < 4; ++j)
				a[i][j] = (j == c || i == j);
	}
};
int n, m; char s[maxn];
struct seg{
	matrix t[maxn << 2 | 1];
	void build(int x, int l, int r){
		if(l == r)return t[x].get(s[l] - 'A');
		int mid = (l + r) >> 1;
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
		t[x] = t[x << 1] * t[x << 1 | 1];
	}
	void modify(int x, int l, int r, int pos, int c){
		if(l == r)return t[x].get(c);
		int mid = (l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos, c);
		else modify(x << 1 | 1, mid + 1, r, pos, c);
		t[x] = t[x << 1] * t[x << 1 | 1];
	}
	matrix query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x];
		int mid = (l + r) >> 1;
		if(L <= mid && R > mid)return query(x << 1, l, mid, L, R) * query(x << 1 | 1, mid + 1, r, L, R);
		if(L <= mid)return query(x << 1, l, mid, L, R);
		else return query(x << 1 | 1, mid + 1, r, L, R);
	}
}T;
int query(int l, int r){
	matrix res; res.a[0][3] = 1;
	res = res * T.query(1, 1, n, l, r);
	int ans = 0; for(int i = 0; i < 3; ++i)ans = (ans + res.a[0][i]) % mod;
	return ans;
}
int main(){
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	n = read(), m = read();
	scanf("%s",s + 1);
	T.build(1, 1, n);
	for(int i = 1; i <= m; ++i){
		int op = read(), l = read();
		if(op & 1){
			scanf("%s",s + 1);
			T.modify(1, 1, n, l, s[1] - 'A');
		}else{
			int r = read();
			printf("%d\n",query(l, r));
		}
	}
	return 0;
}

D. 铺设道路

首先贪心的选择能选择的最长区间,可以得到天数和最大值

这个可以直接笛卡尔树解决

考虑最小值,通过手模样例大眼观察胡乱猜想,感觉好像作为左右端点的位置和次数都不会变

那么计算出每个点作为左右端点出现的次数,然后从前向后贪心配对即可

事实上找端点可以通过差分解决

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 = 3e5 + 55, mod = 1e9 + 7;
int n, a[maxn];
int si[maxn], ls[maxn], rs[maxn], l[maxn], r[maxn];
int cl[maxn], cr[maxn];
int st[maxn], top, root;
int mi, mx;
ll day;
int sq(int x){return 1ll * x * x % mod;}
void dfs(int x, int val){
	si[x] = 1;
	if(ls[x])dfs(ls[x], a[x]), l[x] = l[ls[x]];
	if(rs[x])dfs(rs[x], a[x]), r[x] = r[rs[x]];
	cl[l[x]] += a[x] - val;
	cr[r[x]] += a[x] - val;
	day += a[x] - val;
	mx = (mx + 1ll * (a[x] - val) * sq(r[x] - l[x] + 1) )% mod;
}
int main(){
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)l[i] = r[i] = i;
	for(int i = 1; i <= n; ++i){
		int tmp = top;
		while(tmp && a[st[tmp]] > a[i])--tmp;
		if(tmp)rs[st[tmp]] = i;
		if(tmp != top)ls[i] = st[tmp + 1];
		st[top = ++tmp] = i;
	}
	root = st[1]; dfs(root, 0);
	for(int l = 1, r = 1; l <= n; ++l){
		while(cl[l]){
			while(cr[r] == 0)++r;
			int dt = min(cl[l], cr[r]);
			mi = (mi + 1ll * sq(r - l + 1) * dt) % mod;
			cl[l] -= dt;
			cr[r] -= dt;
		}
	}
	printf("%lld\n",day);
	printf("%d\n",mx);
	printf("%d\n",mi);
	return 0;
}
posted @ 2023-02-27 17:48  Chen_jr  阅读(116)  评论(7编辑  收藏  举报