正睿20秋季提高十连测day4
估分:60+20+30=110
实际:60+20+30=110
T1:
不会,不知道怎么用答案小于50
先正常的DP,dp[i][j]表示将a的前i个字符转换成b的前j个字符的最小步数,因为答案在五十以内,所以只计算两个字符串长度差为五十以内的状态
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 100010, M = 110; 8 const int inf = 0x3f3f3f3f; 9 10 int n, m; 11 char a[N], b[N]; 12 int dp[N][M], ans = inf; 13 14 int main() 15 { 16 scanf("%s%s", a + 1, b + 1); 17 18 n = strlen(a + 1); 19 m = strlen(b + 1); 20 21 memset(dp, 0x3f, sizeof(dp)); 22 dp[0][50] = 0; 23 for (int i = 1; i <= m; i++) 24 { 25 for (int k = 0, tmp = dp[i - 1][k]; k <= 100; k++, tmp = std::min(tmp + 1, dp[i - 1][k])) 26 { 27 if (i + k - 50 >= 0 && i + k - 50 <= n) 28 { 29 dp[i][k] = std::min(dp[i - 1][k + 1] + 1, tmp + (a[i + k - 50] != b[i])); 30 } 31 else 32 { 33 dp[i][k] = inf; 34 } 35 } 36 } 37 38 for (int k = 0; k <= 100; k++) 39 { 40 if (m + k - 50 <= n) 41 { 42 ans = min(ans, dp[m][k] + (n - (m + k - 50))); 43 } 44 } 45 46 printf("%d", ans > 50 ? -1 : ans); 47 }
T2:
不会
用一个大小为200的数组存储2^k的前200位,用一个数组存储k的高精度,可以利用2^(k+l)=2^k*2^l来算。
定义区间[min,max],表示前缀为n个1的值的范围。维护L,R表示当前次幂中前两百位最小的和最大的的前两百位和幂次,初始值为2^1。维护一个数x,初始为1,每次计算x*L,如果x*L前两百位不超过max,则把x乘上L,否则计算L*R,因为L,R,x的初始值都是1,若x*L前两百位大于max时,则计算L*R,所以若L*R的前两百位在[L,R]之间,那在前面一定有x*L小于max,不会运算到这个状态,所以L*R的前两百位不是比L小就是比R大,如果比L小就更新L,比R大就更新R。相当于是不断扩大幂次,L的前两百位是前缀的最小值,R是最大值,在进行x*L和L*R的运算时就在不断增加幂次,所以能算出答案。
(大概是这样吧,我也不清楚,题解的两个容易发现我是真没发现)
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 #define mp make_pai 10 11 typedef long long LL; 12 13 const int N = 100010; 14 const int mxs = 200; 15 const int mod = 998244353; 16 17 void update(vector<int>& a) 18 { 19 for (int i = 0; i < a.size(); i++) 20 { 21 if (a[i] >= 10) 22 { 23 if (i == a.size() - 1) a.pb(a[i] / 10); 24 else a[i + 1] += a[i] / 10; 25 a[i] %= 10; 26 } 27 } 28 if (a.size() > mxs) 29 { 30 for (int j = 0; j < mxs; j++) a[j] = a[a.size() - mxs + j]; 31 a.resize(mxs); 32 } 33 } 34 35 vector<int> operator * (vector<int>& a, vector<int>& b) 36 { 37 vector<int> c(a.size() + b.size() - 1); 38 for (int i = 0; i < c.size(); i++) c[i] = 0; 39 for (int i = 0; i < a.size(); i++) 40 { 41 for (int j = 0; j < b.size(); j++) 42 { 43 c[i + j] += a[i] * b[j]; 44 } 45 } 46 update(c); 47 return c; 48 } 49 50 bool operator < (vector<int>& a, vector<int>& b) 51 { 52 for (int i = a.size() - 1; i >= 0; i--) if (a[i] != b[i]) return a[i] < b[i]; 53 return 0; 54 } 55 56 vector<int> operator + (vector<int>& a, vector<int>& b) 57 { 58 vector<int> c(max(a.size(), b.size())); 59 for (int i = 0; i < c.size(); i++) 60 { 61 c[i] = 0; 62 if (i < a.size()) c[i] += a[i]; 63 if (i < b.size()) c[i] += b[i]; 64 } 65 update(c); 66 return c; 67 } 68 69 LL qmul(LL a, LL k) 70 { 71 LL res = 1; 72 while (k) 73 { 74 if (k & 1) res = res * a % mod; 75 a = a * a % mod; 76 k >>= 1; 77 } 78 return res; 79 } 80 81 struct Node 82 { 83 vector<int> a; 84 vector<int> b; 85 Node() { a.clear(), b.clear(); } 86 Node(vector<int> a, vector<int> b) :a(a), b(b) {} 87 Node operator * (Node c) 88 { 89 return Node(a * c.a, b + c.b); 90 } 91 }; 92 93 Node L, R, res; 94 vector<int> lb, mx; // 满足前缀为n个连续的1的答案范围 95 char inp[mxs]; 96 97 int main() 98 { 99 int n; 100 scanf("%d", &n); 101 102 vector<int> u, v; 103 for (int i = 0; i < mxs - 1; i++) u.pb(0); 104 u.pb(2), v.pb(1); 105 106 for (int i = 0; i < mxs - n; i++) lb.pb(0), mx.pb(9); 107 for (int i = n - 1; i >= 0; i--) lb.pb(1), mx.pb(1); 108 109 L = Node(u, v); // 前两百位 幂次 110 R = Node(u, v); 111 112 v[0] = 0, u[mxs - 1] = 1; 113 res = Node(u, v); 114 115 while (true) 116 { 117 while (true) 118 { 119 Node ed = res * L; 120 if (res.a < ed.a && ed.a < mx) 121 { 122 res = ed; 123 if (!(res.a < lb)) 124 { 125 for (int i = res.b.size() - 1; i >= 0; i--) printf("%d", res.b[i]); 126 return 0; 127 } 128 } 129 else break; 130 } 131 while (true) 132 { 133 Node num = L * R; 134 if (num.a[mxs - 1] == 1) 135 { 136 L = num; 137 break; 138 } 139 else R = num; 140 } 141 } 142 }
T3:
不会,找到了“忽略已打开的灯后不存在相邻的两盏灯颜色一样”这个规律,但不知道怎么用,没想到逆序枚举
从小到大枚举颜色,逆序枚举开灯顺序,满足任意时刻合法的情况下使劲染当前枚举到的颜色(忽略已打开的灯后不存在相邻的两盏灯颜色一样)
1 #include<cstdio> 2 #include<cstdio> 3 #include<set> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N = 20010; 9 10 int n, m; 11 int tim[N], id[N], v[N]; 12 bool vis[N]; 13 set<int> s; 14 15 int main() 16 { 17 scanf("%d%d", &n, &m); 18 for (int i = 1; i <= n; i++) scanf("%d", &tim[i]); 19 20 int col = 0; 21 while (true) 22 { 23 int len = 0; 24 for (int i = n; i; i--) 25 { 26 if (!id[tim[i]]) v[++len] = tim[i]; 27 } 28 if (!len) break; 29 col++; 30 s.insert(0), s.insert(n + 1); 31 for (int i = 1; i <= len; i++) 32 { 33 s.insert(v[i]); 34 vis[v[i]] = false; 35 } 36 for (int i = 1; i <= len; i++) 37 { 38 if (!vis[v[i]]) 39 { 40 id[v[i]] = col; 41 vis[*--s.lower_bound(v[i])] = true; 42 vis[*s.upper_bound(v[i])] = true; 43 } 44 s.erase(v[i]); 45 } 46 } 47 for (int i = 1; i <= n; i++) printf("%d ", id[i]); 48 }
总结:
这次就完全是不会写了,并没有失误。需要注意的就是关于题目的一些数据还是要好好利用。
浙公网安备 33010602011771号