算法复键——5道ABC F题

ABC 462 F(动态规划)

显然dp

设 dp(i,j,k)dp(i,j,k)dp(i,j,k) 表示前 iii 个字符,目前的贡献为 jjj,最后两个字母为 kkk 的方案数。

jjj 最大为 KKK, 但可能为负,但顶多-1.

kkk 的话其实本质只需要区分三种状态 ABABAB、XAXAXA、XXXXXX。只有这三种状态是必要的

然后转移的话,我们在每个 ABC 的末尾计算贡献。如果本来可以,现在不行,就-1。如果本来不行,现在可以,就+1

done...

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
#define N 300010
//#define mo
#define M 15
int n, m, i, j, k, T;
int f[M][3], g[M][3], ans, mx, a[N], K, n1, n2; 
char s[N]; 

int Z1(int x) {
	return x + 2; 
}

int Z2(char a, char b) {
	if(a == 'A' && b == 'B') return 0; 
	if(b == 'A') return 1; 
	return 2; 
}

int N2(int k, char c) {
	if(k == 1 && c == 'B') return 0; 
	if(c == 'A') return 1; 
	return 2; 
}

int N1(int i, int j, int k, char c, int op) {
	int cnt = 0; 
	if(a[i]) cnt = -1; 
	if(k == 0 && c == 'C') ++cnt; 
	j += cnt; 
	if(j < -2 || j > K) return -1; 
//	if(cnt == 0 && op == 1) return -1; 
	return j + 2; 
}

void Min(int &a, int b) {
	a = min(a, b); 
}

signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
	T = read();
	while(T--) {
		scanf("%s", s + 1); n = strlen(s + 1); 
		for(i = 3; i <= n; ++i) {
			if(s[i] == 'C' && s[i - 1] == 'B' && s[i - 2] == 'A') a[i] = 1; 
			else a[i] = 0; 
		}
//		for(i = 1; i <= n; ++i) debug("%d ", a[i]); debug("\n"); 
		K = read(); 
		memset(f, 0x3f, sizeof(f)); 
		memset(g, 0x3f, sizeof(g)); 
		f[Z1(0)][2] = 0; mx = f[Z1(0)][0]; 
		for(i = 1; i <= n; ++i) {
			for(j = -2; j <= K; ++j) {
				for(k = 0; k <= 2; ++k) {
					if(f[Z1(j)][k] == mx) continue; 
//					debug("[%d](%d %d) %d\n", i - 1, j, k, f[Z1(j)][k]); 
					for(char c = 'A'; c <= 'C'; ++c) {
						n1 = N1(i, j, k, c, 1); 
						n2 = N2(k, c); 
						if(n1 == mx) continue; 
						Min(g[n1][n2], f[Z1(j)][k] + 1); 
//						debug("Go to %d %d [%d]\n", n1, n2, g[n1][n2]); 
					}
					n1 = N1(i, j, k, s[i], 0); 
					n2 = N2(k, s[i]); 
					if(n1 == mx) continue; 
					Min(g[n1][n2], f[Z1(j)][k]); 
				}
			}
			for(j = -2; j <= K; ++j) 	
				for(k = 0; k <= 2; ++k) {
//					debug("g[%d][%d] = %d\n", j, k, g[j][k]); 
					f[Z1(j)][k] = g[Z1(j)][k], g[Z1(j)][k] = mx; 
				}
		}
		for(k = 0, ans = mx; k <= 2; ++k) ans = min(ans, f[Z1(K)][k]); 
		printf("%d\n", ans == mx ? -1 : ans); 
	}

	return 0;
}

ABC 461 F(搜索、计数)

不难发现,合法的状态数(在不考虑排列的情况下)必然不多,于是我们可以预处理因子后直接搜索,搜索过程记录当前有多少个数和数的和。最后拿和乘以排列数即可。

但直接这样子做会T掉,我们尝试把数从大往小排序,然后就过了

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
#define mo 998244353
#define N 2010
int n, m, i, j, k, T;
int fac[N], ans; 
unordered_map<int, bool>mp;  
vector<int>v, v2; 

void dfs(int n, int stp, int sum, int i) {	
	for(; i < m; ++i) {
		if(n == v[i]) {
			ans += (sum + n) % mo * fac[stp + 1] % mo; 
			ans %= mo; 
		}
		if(n % v[i] == 0) {
			dfs(n / v[i], stp + 1, sum + v[i], i + 1); 
		}
	}
}

signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
//	T = read();
//	while(T--) {
//
//	}
	n = read(); 
	for(i = fac[0] = 1; i <= 2000; ++i) 
		fac[i] = fac[i - 1] * i % mo; 
	for(i = 1; i * i <= n; ++i) {
		if(n % i == 0 && i * i != n) v.pb(i), v.pb(n / i); 
		else if(n % i == 0) v.pb(i);  
	} 
	m = v.size(); 
	sort(v.begin(), v.end()); 
	reverse(v.begin(), v.end()); 
	dfs(n, 0, 0, 0);  
	printf("%lld\n", ans); 
	return 0;
}

ABC 460 F(树、dfs序、线段树)

题目本质是动态求树的直径。

动态求树的直径是套路,先跑出dfn序,然后用线段树维护即可。

具体得,我们要维护区间最深点、区间最浅点,区间答案,区间左+中最大值,区间中+右最大值。

用这几个就能拼凑出答案了。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
//#define mo
#define N 100010
int n, m, i, j, k, T; 
int x, y; 
int dep[N], dfn[N << 2], q, rt, cnt; 
int c[N << 2], cale[N << 2]; 
vector<int>v[N]; 

void dfs(int x, int fa) {
	dfn[++cnt] = x; dep[x] = dep[fa] + 1; 
	c[x] = cnt; cale[cnt] = 1; 
	for(int y : v[x]) 
		if(y != fa) {
			dfs(y, x);  
			dfn[++cnt] = x; 
		}
}

struct Segment_tree {
	int tot, ls[N << 4], rs[N << 4]; 
	int ans[N << 4], depmx[N << 4], depmn[N << 4], ld[N << 4], rd[N << 4]; 
	int legal[N << 2]; 
	void push_up(int k) {
//		legal[k] = (legal[ls[k]] | legal[rs[k]]); 
//		if(!legal[k]) return ; 
//		int x = 0; 
//		if(!legal[ls[k]]) x = rs[k]; 
//		if(!legal[rs[k]]) x = ls[k]; 
//		if(x) {
//			ans[k] = ans[x]; 
//			depmx[k] = depmx[x]; depmn[k] = depmn[x]; 
//			ld[k] = ld[x]; rd[k] = rd[x]; 
//			
//		}
		ans[k] = max(ans[ls[k]], ans[rs[k]]); 
		depmx[k] = max(depmx[ls[k]], depmx[rs[k]]); 
		depmn[k] = min(depmn[ls[k]], depmn[rs[k]]); 
		ans[k] = max(ans[k], rd[ls[k]] + depmx[rs[k]]); 
		ans[k] = max(ans[k], depmx[ls[k]] + ld[rs[k]]); 
		ld[k] = max(max(ld[ls[k]], ld[rs[k]]), depmx[rs[k]] - 2 * depmn[ls[k]]); 
		rd[k] = max(max(rd[ls[k]], rd[rs[k]]), depmx[ls[k]] - 2 * depmn[rs[k]]); 
	}
	void build(int &k, int l, int r) {
		if(!k) k = ++tot; 
		if(l == r) {
			ans[k] =  0; legal[k] = 1; 
			depmn[k] = dep[dfn[l]]; 
			if(cale[l]) depmx[k] = dep[dfn[l]], ld[k] = rd[k] = -dep[dfn[l]]; 
//			debug(">> [%d] %d %d\n", l, depmx[k], depmn[k]); 
//			if(cale[l]) debug("=== %d\n", l); 
			else depmx[k] = ld[k] = rd[k] = - 5 * n; 
			return ; 
		}
		int mid = (l + r) >> 1; 
		build(ls[k], l, mid); 
		build(rs[k], mid + 1, r); 
		push_up(k); 
//		debug("[%d %d] mx %d mn %d ans : %d\n", l, r, depmx[k], depmn[k], ans[k]); 
//		debug("[%d %d][%d %d]\n", depmx[ls[k]], depmx[rs[k]], depmn[ls[k]], depmn[rs[k]]); 
	}
	void modify(int k, int l, int r, int x) {
		if(l == r) {
			if(legal[k]) {
				depmx[k] = ld[k] = rd[k] = - 5 * n; 
			}
			else {
				depmx[k] = dep[dfn[l]]; 
				ld[k] = rd[k] = -dep[dfn[l]]; 
			}
			legal[k] = 1 - legal[k]; 
			return ; 
		}
		int mid = (l + r) >> 1; 
		if(x <= mid) modify(ls[k], l, mid, x); 
		else modify(rs[k], mid + 1, r, x); 
		push_up(k); 
		debug("[%d %d] mx %d mn %d ans : %d || %d %d\n", l, r, depmx[k], depmn[k], ans[k], ld[k], rd[k]); 
//		debug("[%d %d][%d %d]\n", depmx[ls[k]], depmx[rs[k]], depmn[ls[k]], depmn[rs[k]]); 
	}
}Seg; 

signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
//	T = read();
//	while(T--) {
//
//	}
	n = read(); 
	for(i = 1; i < n; ++i) {
		x = read(); y = read(); 
		v[x].pb(y); v[y].pb(x); 
	}
	dfs(1, 0); 
	for(i = 1; i <= cnt; ++i) debug("%2d ", i); debug("\n"); 
	for(i = 1; i <= cnt; ++i) debug("%2d ", dfn[i]); debug("\n"); 
	for(i = 1; i <= cnt; ++i) debug("%2d ", dep[dfn[i]]); debug("\n"); 
	for(i = 1; i <= n; ++i) debug("%d ", dep[i]); debug("\n"); 
	Seg.build(rt, 1, cnt); 
	q = read(); 
	while(q--) {
		x = read(); 
		Seg.modify(rt, 1, cnt, c[x]); 
		printf("%d\n", Seg.ans[rt]); 
	} 
	return 0;
}

ABC 459 F(贪心、单调队列)

先减各自序号,把题目转化为单调不下降

我们可以直接贪心,用单调队列维护连续段。

每次取出末尾一段,然后和当前拼接成一段或两段

可以发现,只有如下三种情况:

各自处理即可

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
//#define mo 
//#define N
#define E9 1000000000
int n, m, i, j, k, T; 
int ans, x, l1, l2, lx, h1, h2, hx, nh1, nh2, nl1, nl2; 
int S, L; 
stack<pair<int, int> >z; 

int count(int a, int b, int c) {
	debug("count[%lld %lld] * %lld\n", a, b, c); 
	if(a < 0 || b < 0 || c < 0) return 0; 
	return (a * (a - 1) / 2 + a * c) * b;  
}

signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
	T = read();
	while(T--) {
		n = read(); ans = 0; 
		while(!z.empty()) z.pop(); 
		for(i = 1; i <= n; ++i) {
			x = read() - i + E9; 
//			printf("%d ", x - E9); 
			if(z.empty() || x > z.top().fi) z.push({x, 1}); 
			else if(x == z.top().fi) {
				auto t = z.top(); z.pop(); 
				z.push({t.fi, t.se + 1}); 
			}
			else {
				debug("For i = %lld : \n", i); 
				h1 = x; l1 = 1; h2 = E9; l2 = 0; 
				while(!z.empty() && h1 <= z.top().fi) {
					auto t = z.top(); z.pop(); 
					hx = t.fi; lx = t.se; 
					debug("[%lld %lld] [%lld %lld][%lld %lld] ==> ", hx - E9, lx, h1 - E9, l1, h2 - E9, l2); 
					S = hx * lx + h1 * l1 + h2 * l2; 
					L = lx + l1 + l2; 
					nh1 = S / L; nh2 = nh1 + 1; 
					nl2 = S % L; nl1 = L - nl2; 
					debug("[%lld %lld][%lld %lld]\n", nh1 - E9, nl1, nh2 - E9, nl2); 
					if(nl1 <= lx) {
						ans += count(nl1, hx - nh1, lx - nl1 + 1); 
						ans += count(lx - nl1, hx - nh2, 1); 
						ans += count(l1, nh2 - h1, 0); 
						if(l2) ans += count(l2, nh2 - h2, l1); 
					} 
					else if(nl1 <= lx + l1) {
						ans += count(lx, hx - nh1, 1); 
						ans += count(nl1 - lx, nh1 - h1, 0); 
						ans += count(lx + l1 - nl1, nh2 - h1, nl1 - lx); 
						if(l2) ans += count(l2, nh2 - h2, l1); 
					}
					else {
						ans += count(lx, hx - nh1, 1); 
						ans += count(l1, nh1 - h1, 0); 
						ans += count(nl1 - l1 - lx, nh1 - h2, l1); 
						if(l2) ans += count(nl2, nh2 - h2, nl1 - lx); 
					}
					debug("		now ans is %lld\n", ans); 
					l1 = nl1; h1 = nh1; l2 = nl2; h2 = nh2; 
				}
				z.push({h1, l1}); if(l2) z.push({h2, l2}); 
			}
			debug("now ans is %lld\n", ans); 
		}	
//		debug("\n"); 
		printf("%lld\n", ans); 
	}

	return 0;
}

ABC 458 F(AC自动机、矩阵快速幂)

nnn 极大,显然要矩阵快速幂

矩阵的构建过程拿AC自动机即可

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
#define mo 998244353
#define N 110
int n, m, i, j, k, T, rt;
char str[N]; 

void Add(int &a, int b) {
	b %= mo; 
	a = ((a + b) % mo + mo) % mo; 
}

struct Matrix {
	int i, j, k; 
	int a[N][N]; 
	void clear() {
		memset(a, 0, sizeof(a)); 
	}
	void add(int x, int y, int z) {
		a[x][y] = z; 
	}
	void print() {
		#ifdef LOCAL
		 	for(i = 1; i <= n; ++i, debug("\n"))
		 		for(j = 1; j <= n; ++j)
		 			debug("%lld ", a[i][j]); 
		 	debug("----------\n"); 
		#endif
	}
//	Matrix operator * (const Matrix &B) {
//		Martix C; C.clear(); 
//		for(i = 1; i <= n; ++i) 
//			for(j = 1; j <= n; ++j) 
//				for(k = 1; k <= n; ++k) 
//					Add(C.a[i][k], a[i][j] * B.a[j][k]); 
//		return C;  
//	}
}ans, A; 

void MUL(Matrix &A, Matrix B, int nx) {
	int i, j, k; Matrix C; C.clear();  
	for(i = 1; i <= nx; ++i)
		for(j = 1; j <= n; ++j) 
			for(k = 1; k <= n; ++k) 
				Add(C.a[i][k], A.a[i][j] * B.a[j][k]); 
	A = C; 
}

void Run(int b) {
	ans.a[1][1] = 1; 
	while(b) {
		if(b & 1) MUL(ans, A, 1); 
		MUL(A, A, n); b >>= 1;  
		A.print(); 
	}
	int sum = 0; 
//	ans.print(); 
	for(i = 1; i <= n; ++i) Add(sum, ans.a[1][i]); 
	printf("%lld", sum); 
}

struct Trie {
	int ch[N][27], fail[N], End[N], vis[N]; 
	queue<int>q; 
	void add(int &x, int i) {
		if(!x) x = ++n; 
		if(!str[i + 1]) End[x] = 1;  
		if(str[i + 1]) add(ch[x][str[i + 1] - 'a'], i + 1); 
//		debug("%lld -> %lld[%c] | %lld\n", x, ch[x][str[i] - 'a'], str[i], i); 
//		if(str[i + 2]) A.add(x, son[x][str[i + 1] - 'a'], 1); 	
	}
	void bfs() {
		q.push(1); fail[1] = 0; fail[0] = 1; 
		while(!q.empty()) {
			int u = q.front(), v; q.pop(); 
			for(i = 0; i < 26; ++i) {
				if(!(v = ch[u][i]) || fail[v]) continue; 
				k = fail[u]; 
				fail[v] = (ch[k][i] ? ch[k][i] : 1); 
				End[v] |= End[fail[v]]; 
				for(j = 0; j < 26; ++j) 
					if(!ch[v][j]) ch[v][j] = ch[fail[v]][j]; 
				q.push(v); 
			}
		}
	}
	void run(int x) {
		int k = 26; vis[x] = 1; 
		for(int i = 0; i < 26; ++i) {
//			if(ch[x][i]) debug("%lld --> %lld\n", x, ch[x][i]); 
			if(ch[x][i] > 1) {
				--k; 
				if(!End[ch[x][i]]) A.add(x, ch[x][i], 1); 
				if(!vis[ch[x][i]]) run(ch[x][i]); 
			}
		}
		A.add(x, rt, k); 
	}
}Tr;

signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
//	T = read();
//	while(T--) {
//
//	}
	int Times; 
	Times = read(); m = read(); 
	rt = n = 1; 
	for(i = 1; i <= m; ++i) {
		scanf("%s", str + 1); 
		Tr.add(Tr.ch[rt][str[1] - 'a'], 1); 
//		A.add(rt, Tr.ch[rt][str[1] - 'a'], 1); 
	}
	Tr.bfs(); 
	Tr.run(rt); 
//	A.print(); 
	Run(Times); 
	return 0;
}