正睿20秋季普转提day3
估分:100+100+30+40=270
实际:100+100+30+40=270
T1:
k<=n/2 直接枚举
k>n/2 字符串s从1枚举到k,找出字符串中应该与s[i]一样的字符,需要变换的次数就是这些字符总数减去出现次数最多的字符的出现次数(为了防止出错所以写了个暴力保底75pts)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 100010; 8 9 int n, k; 10 int vis[50]; 11 char str[N], str1[N]; 12 bool st[N]; 13 14 int get_ans(int x) 15 { 16 int res = 0; 17 for (int i = 1; i <= n; i++) if (str1[i] != str[i]) res++; 18 return res; 19 } 20 21 bool check(int x) 22 { 23 for (int i = 1, j = n - k + 1; i <= k; i++, j++) 24 { 25 if (str1[i] != str1[j]) return false; 26 } 27 return true; 28 } 29 30 void get_char(int x) 31 { 32 for (int i = 0, j = n; i < n; i++, j--) 33 { 34 str1[j] = ((x >> i) & 1) + 'a'; 35 } 36 } 37 38 void get_50pts() 39 { 40 int ans = 0x3f3f3f3f; 41 for (int i = 0; i < (1 << n); i++) 42 { 43 get_char(i); 44 if (check(i)) 45 { 46 ans = min(ans, get_ans(i)); 47 } 48 } 49 printf("%d", ans); 50 } 51 52 int get(int x) 53 { 54 for (int i = 0; i <= 26; i++) vis[i] = false; 55 56 int res = 1, Ma = 0; 57 int j = x; 58 st[j] = true; 59 vis[str[j] - 'a']++; 60 while (j <= k) 61 { 62 res++; 63 j = n - k + j; 64 st[j] = true; 65 vis[str[j] - 'a']++; 66 if (vis[str[j] - 'a'] > Ma) Ma = vis[str[j] - 'a']; 67 } 68 return res - Ma; 69 } 70 71 int main() 72 { 73 scanf("%d%d", &n, &k); 74 scanf("%s", str + 1); 75 76 bool flag = true; 77 for (int i = 1; i <= n; i++) 78 { 79 if (str[i] != 'a' && str[i] != 'b') 80 { 81 flag = false; 82 break; 83 } 84 } 85 86 if (n <= 20 && k <= 20 && flag) 87 { 88 get_50pts(); 89 return 0; 90 } 91 92 if (k <= n / 2) 93 { 94 int ans = 0; 95 for (int i = 1, j = n - k + 1; i <= k; i++, j++) 96 { 97 if (str[i] != str[j]) ans++; 98 } 99 printf("%d", ans); 100 return 0; 101 } 102 103 int ans = 0; 104 for (int i = 1; i <= k; i++) 105 { 106 if (st[i]) continue; 107 ans += get(i); 108 } 109 printf("%d", ans); 110 }
T2:
对于每一位,至少得有一个0、两个1才能判断是什么运算,所以对于每一位记录一下就行了
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 inline int read() 8 { 9 int x = 0, f = 0; 10 char ch = getchar(); 11 while (!isdigit(ch)) f = ch == '-', ch = getchar(); 12 while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); 13 return f ? -x : x; 14 } 15 16 const int N = 510; 17 18 int st[N][5], ned[N]; 19 char str[N]; 20 21 int main() 22 { 23 int T = read(); 24 while (T--) 25 { 26 int n = read(), m = read(); 27 memset(st, 0, sizeof(st)); 28 for (int i = 1; i <= n; i++) 29 { 30 scanf("%s", str + 1); 31 for (int i = 1; i <= m; i++) 32 { 33 st[i][str[i] - '0']++; 34 } 35 } 36 37 int ans = 0; 38 for (int i = 1; i <= m; i++) 39 { 40 ned[i] = max(0, (1 - st[i][0])) + max(0, (2 - st[i][1])); 41 ans = max(ans, ned[i]); 42 } 43 printf("%d\n", ans); 44 } 45 }
T3:
不会,虽然暴力有50pts,但不会写
状压DP,f[i][s]表示前i个数使用的质因子的状态为s,更新答案时记得记录一下当前位置当前状态放的数是什么,因为a[i]最多到30,所以能变换的数最大到58,59及以上还不如直接变成1。因为最大取到58,所以会用到的质因数只有16个
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 #define pb push_back 9 10 const int N = 110, m = 16; 11 const int pri[20] = { 0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53 }; 12 13 int n; 14 int a[N], di[N]; 15 int f[N][1 << m], opt[N][1 << m]; 16 17 vector<int> b; 18 19 int main() 20 { 21 scanf("%d", &n); 22 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 23 24 for (int i = 1; i < 59; i++) 25 { 26 for (int j = 1; j <= 16; j++) 27 { 28 if (!(i % pri[j])) 29 { 30 di[i] |= 1 << (j - 1); 31 } 32 } 33 } 34 35 memset(f, 0x3f, sizeof(f)); 36 f[0][0] = 0; 37 for (int i = 0; i < n; i++) 38 { 39 for (int s = 0; s < (1 << m); s++) 40 { 41 for (int j = 1; j < 59; j++) 42 { 43 if (!(s & di[j]) && f[i + 1][s | di[j]] > f[i][s] + abs(a[i + 1] - j)) 44 { 45 f[i + 1][s | di[j]] = f[i][s] + abs(a[i + 1] - j); 46 opt[i + 1][s | di[j]] = j; 47 } 48 } 49 } 50 } 51 52 int ans = 0x3f3f3f3f, sta; 53 for (int s = 0; s < (1 << m); s++) 54 { 55 if (ans > f[n][s]) 56 { 57 ans = f[n][s]; 58 sta = s; 59 } 60 } 61 62 for (int i = n; i; i--) 63 { 64 b.pb(opt[i][sta]); 65 sta ^= di[opt[i][sta]]; 66 } 67 68 reverse(b.begin(), b.end()); 69 for (auto i : b) printf("%d ", i); 70 }
T4:
不会
枚举中间数a[i],维护一个数组h,h[j]=1表示j在1-i出现过,否则就没出现过。len为min(a-1,n-a),表示若存在两个数x,y使得(x+y)/2=a[i],x和y的范围。然后判断h[a-len,a-1]和h[a+1,a+len],若存在x,y分别存在[a-len,a-1]和[a+1,a+len]里,且(x+y)/2=a[i],若h[x]和h[y]的值不同,则输出YES,如果枚举完a了还是没有就输出NO。
证明:若h[x]和h[y]都为1则说明x和y都在1-i之间出现过,都在i的左边;若都为0则都没出现过,都在i的右边;不同的话说明一个在左一个在右,就存在(x+y)/2=a[i]。
若(x+y)/2=a[i],则x,y在h里的下标应该是关于a[i]对称的,因为a[i]是x,y的中间数,所以只需把h[a+1,a+len]倒序,然后判断h[a-len,a-1]和倒序的h[a+1,a+len]对应的每一位是否都相同,如果有不相同的就输出YES,否则继续枚举a。因为h是01串,所以可以把h看作字符串,然后hash一下判断相等。
所以要维护两个权值线段树,一个存储h,一个存储h的逆序,每次枚举a判断,每次枚举后把a[i]插到线段树里去
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 typedef unsigned long long ULL; // 取unsigned long long 因为溢出会自动取模 8 9 const int N = 300010; 10 const ULL bas = 13131; 11 12 int n, a[N]; 13 ULL pos_tre[N << 2], rev_tre[N << 2]; // 正序 逆序 14 ULL power[N]; // hash进制 15 16 void build(int q, int l, int r) 17 { 18 pos_tre[q] = rev_tre[q] = 0; 19 if (l == r) return; 20 int mid = (l + r) >> 1; 21 build(q << 1, l, mid); 22 build(q << 1 | 1, mid + 1, r); 23 } 24 25 ULL query_pos(int q, int l, int r, int L, int R) 26 { 27 if (L <= l && r <= R) return pos_tre[q]; 28 int mid = (l + r) >> 1; 29 if (L <= mid && R > mid) return(power[min(R, r) - mid] * query_pos(q << 1, l, mid, L, R) + query_pos(q << 1 | 1, mid + 1, r, L, R)) ; 30 if (L > mid) return query_pos(q << 1 | 1, mid + 1, r, L, R); 31 if (R <= mid) return query_pos(q << 1, l, mid, L, R); 32 } 33 34 ULL query_rev(int q, int l, int r, int L, int R) 35 { 36 if (L <= l && r <= R) return rev_tre[q]; 37 int mid = (l + r) >> 1; 38 if (L <= mid && mid < R) return (power[mid - max(l, L) + 1] * query_rev(q << 1 | 1, mid + 1, r, L, R) + query_rev(q << 1, l, mid, L, R)) ; 39 if (R <= mid) return query_rev(q << 1, l, mid, L, R); 40 if (L > mid) return query_rev(q << 1 | 1, mid + 1, r, L, R); 41 } 42 43 void pushup(int q, int l, int r) 44 { 45 int mid = (l + r) >> 1; 46 pos_tre[q] =(pos_tre[q << 1] * power[r - mid] + pos_tre[q << 1 | 1]) ; 47 rev_tre[q] = (rev_tre[q << 1 | 1] * power[mid - l + 1] + rev_tre[q << 1]) ; 48 } 49 50 void modify(int q, int l, int r, int x, ULL v) 51 { 52 if (l == r) 53 { 54 pos_tre[q] = rev_tre[q] = v; 55 return; 56 } 57 58 int mid = (l + r) >> 1; 59 if (x <= mid) modify(q << 1, l, mid, x, v); 60 else modify(q << 1 | 1, mid + 1, r, x, v); 61 pushup(q, l, r); 62 } 63 64 void init() 65 { 66 power[0] = 1; 67 for (int i = 1; i < N; i++) power[i] = power[i - 1] * bas; 68 } 69 70 int main() 71 { 72 init(); 73 74 int T; 75 scanf("%d", &T); 76 while (T--) 77 { 78 scanf("%d", &n); 79 build(1, 1, n); 80 bool flag = false; 81 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 82 for (int i = 1; i <= n; i++) 83 { 84 int x = a[i]; 85 if (x != 1 && x != n) 86 { 87 int len = min(x - 1, n - x); 88 if (query_rev(1, 1, n, x - len, x - 1) != query_pos(1, 1, n, x + 1, x + len)) 89 { 90 puts("YES"); 91 flag = true; 92 } 93 } 94 if (flag) break; 95 modify(1, 1, n, x, 1); 96 } 97 if (!flag) puts("NO"); 98 } 99 }
总结:
状压DP还是不大行,关于题目的一些规律不会用,还得多写题。第四题没看见n个数互不相同,所以思路卡着了,读题也要仔细一点。
浙公网安备 33010602011771号