https://ac.nowcoder.com/acm/contest/11169
D - 回文字D
 
题意:定义D型回文串:若长度小于D,则必是;否则,对于任意长度为D的连续子串,必须是回文串。给定D,串 S,要分割成 k 个连续子串,满足每个子串都是D型回文串,问最小的 k。
贪心+字符串哈希
假设用dp,则 dp[i]=j<i且S[j+1⋯i]为D型回文串min(dp[j])+1。
又容易发现 dp[i] 必是递增的,所以上面这个dp实际上就是贪心选最长的D型回文串 S[j⋯i]。
所以直接从头开始匹配最长的D型回文串即可。
匹配固定长度D的回文串可以用字符串哈希,正着哈希值等于反着哈希值,说明是回文串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
   | #include <bits/stdc++.h> #define debug(x) cout << #x << ":\t" << (x) << endl; using namespace std; #define ll long long #define ull unsigned ll const int N = 1e7 + 10; const int INF = 0x3f3f3f3f; const ll inf = 0x3f3f3f3f3f3f3f3f; const ll mod = 1e9 + 7; const ll inv2 = (mod + 1) / 2; const int maxm = 1e6 + 10; int n, d; char s[N]; ull h[N], rh[N], p[N], P = 131; ull hsh(int l, int r, ull* h) { 	return h[r] - h[l - 1] * p[r - l + 1]; } ull rhsh(int l, int r, ull* h) { 	return h[l] - h[r + 1] * p[r - l + 1]; } int main() { 	scanf("%d%d", &n, &d); 	scanf("%s", s + 1); 	p[0] = 1; 	for (int i = 1; i <= n; i++) { 		h[i] = h[i - 1] * P + s[i] - 'a'; 		p[i] = p[i - 1] * P; 	} 	for (int i = n; i >= 1; i--) { 		rh[i] = rh[i + 1] * P + s[i] - 'a'; 	} 	int ans = 0, L = 1, R = 1; 	while (L <= n) { 		while (R <= n && (R - L + 1 < d || hsh(R - d + 1, R, h) == rhsh(R - d + 1, R, rh)))R++; 		ans++; 		L = R; 	} 	printf("%d\n", ans); 	return 0; }
   | 
 
 
E - 小G的数学难题
 
题意:给定长度为 1≤n≤1000 的数列 a,b,c,正整数 1≤P≤10000,且有 ai≤bi,选出一个下标集合 S,满足 i∈S∑ai≤P 且 i∈S∑bi≥P,且最小化 i∈S∑ci.
dp+单调队列
dp[i][j] 表示前 i 个数,∑a≤j 且 ∑b≥j,最小的 ∑c.
上面就是最关键最妙的一步了,假设现在是 dp[i][j],那么加上 a[i+1] 后,有 (∑a)′≤j+a[i+1],(∑b)′≥j+a[i+1],且 (∑a)′≤j+b[i+1],(∑b)′≥j+b[i+1],并且在 [j+a[i+1],j+b[i+1]] 也都是如此。也就是说可以从 dp[i][j] 更新到 dp[i+1][k],k∈[j+ai+1,j+bi+1],换一下就是 dp[i][j] 由 dp[i−1][k],k∈[j−bi,j−ai] 转移得到。
枚举 i,可以发现 [j−bi,j−ai] 这个区间是随着 j 增加而向右移动的,所以通过单调队列维护这个范围内的 dp[i][k] 的最小值。当然要是用个multiset也可以,但是复杂度多了个 log。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
   | #include <bits/stdc++.h> #define debug(x) cout << #x << ":\t" << (x) << endl; using namespace std; #define ll long long #define ull unsigned ll const int N = 2e5 + 10; const int INF = 0x3f3f3f3f; const ll inf = 0x3f3f3f3f3f3f3f3f; const ll mod = 1e9 + 7; const ll inv2 = (mod + 1) / 2; const int maxm = 1e6 + 10; int T; int n, P; int a[N], b[N], c[N]; int dp[1010][10010]; int st, ed; int que[N]; int main() { 	scanf("%d", &T); 	while (T--) { 		scanf("%d%d", &n, &P); 		for (int i = 1; i <= n; i++)scanf("%d", &a[i]); 		for (int i = 1; i <= n; i++)scanf("%d", &b[i]); 		for (int i = 1; i <= n; i++)scanf("%d", &c[i]); 		memset(dp, 0x3f, sizeof(dp)); 		dp[0][0] = 0; 		for (int i = 1; i <= n; i++) { 			st = ed = 0; 			for (int j = 0; j <= P; j++) { 				dp[i][j] = dp[i - 1][j]; 				if (j < a[i])continue; 				while (ed > st&&dp[i - 1][que[ed - 1]] >= dp[i - 1][j - a[i]])ed--; 				que[ed++] = j - a[i]; 				while (ed > st && que[st] < j - b[i])st++; 				dp[i][j] = min(dp[i][j], dp[i - 1][que[st]] + c[i]); 			} 		} 		if (dp[n][P] == INF)puts("IMPOSSIBLE!!!"); 		else printf("%d\n", dp[n][P]); 	} 	return 0; }
   |