div2
250pts MiddleCode
题意:s串长度为奇数时,将中间字符取掉并添加到t末尾;长度为偶数时,将中间两个较小的字符取掉并添加到末尾。
分析:直接做,学习了一下substr(s, pos, len)返回s中从pos开始的长度为len的字串。
代码:
1 class MiddleCode { 2 public: 3 void Remove(string &s, int pos) { 4 int len = s.size(); 5 string t = ""; 6 if(pos < 0 || pos >= len) return; 7 for(int i = 0; i < len; i++) 8 if(i != pos) t += s[i]; 9 s = t; 10 } 11 string encode(string s) { 12 int n = s.size(), m = n; 13 string ans = ""; 14 for(int i = 0; i < n; i++) { 15 if((n-i)&1) { 16 ans += s[(n-i)/2]; 17 Remove(s, (n-i)/2); 18 } else { 19 if(s[(n-i)/2] < s[(n-i)/2-1]) { 20 ans += s[(n-i)/2]; 21 Remove(s, (n-i)/2); 22 } else { 23 ans += s[(n-i)/2-1]; 24 Remove(s, (n-i)/2-1); 25 } 26 } 27 } 28 return ans; 29 } 30 };
500pts SplitIntoPairs
题意:将N个数(偶数)分成N/2组,使得两个数的乘积>=X的组数尽量多,X < 0。
分析:X < 0,所以只有当每组的两个数A,B一正一负时才有可能比X小,将N个数分成负数和非负数两组,如果负数有偶数个,那么结果就是N/2组,
因为负数,正数可以分别两两配对。当为奇数的时候,负数和正数两两配对正好剩下一个,且绝对值应该尽量小,判断两数之积和X的关系即可。
代码:
1 class SplitIntoPairs { 2 public: 3 int makepairs(vector <int> A, int X) { 4 // int n = sz(A); 5 sort(A.begin(), A.end()); 6 vector<int> B, C; 7 for(int i = 0; i < sz(A); i++){ 8 if(A[i] >= 0) B.pb(A[i]); 9 else C.pb(A[i]); 10 } 11 int n = sz(B), m = sz(C); 12 if(n%2 == 0) return (n+m)/2; 13 return (n+m)/2-(1LL*C[m-1]*B[0] < X); 14 } 15 };
950pts GraphWalkWithProbabilities
题意:从一点出发,每一轮选择任意可达的点,该点有win[i], lose[i], 1-win[i]-lose[i]三个概率, 表示到达该点赢,输,继续的概率,从Start出发,
按照最优的走法,最后赢d的概率。
分析:从某一点x出发能够赢得概率和转移到相邻的点y,然后赢的概率有关,但是图中可能存在环,因此采用记忆化搜索的话,
会存在相互依赖关系构成环的情况,转移到一个点y要能够继续进行的话概率为-win[y]-lose[y],那么可以设从y出发,最多进行steps轮,最后能够赢得概率。
这样转移dp[node][steps] = max{ win[to] + (1-win[to]-lose[to]) * dp[to][steps-1] };
steps上界设为3000左右即可,因为1-win[to]-lose[to]最大0.99, 最多3000轮,最终赢得概率应该是能满足题目精度要求的。
代码:
1 const int maxn = 3000 + 10; 2 double dp[55][maxn]; 3 4 class GraphWalkWithProbabilities { 5 public: 6 vector<int> win, lose; 7 vector<int> g[55]; 8 9 double dfs(int node, int steps) { 10 double &res = dp[node][steps]; 11 if(!(res < 0)) return res; 12 res = 0; 13 for(int to: g[node]) 14 res = max(res, win[to]/100.0 + (100-win[to]-lose[to])/100.0*dfs(to, steps-1)); 15 return res; 16 } 17 double findprob(vector <string> graph, vector <int> winprob, vector <int> loseprob, int Start) { 18 for(int i = 0; i < 55; i++) for(int j = 0; j < maxn; j++) 19 dp[i][j] = -1.0; 20 // bug(1) 21 for(int i = 0; i < 55; i++) dp[i][0] = 0.0; 22 win = winprob; 23 lose = loseprob; 24 // bug(1) 25 int n = sz(graph); 26 for(int i = 0; i < n; i++){ 27 for(int j = 0; j < n; j++) 28 if(graph[i][j] == '1') 29 g[i].pb(j); 30 } 31 dfs(Start, maxn-1); 32 return dp[Start][maxn-1]; 33 } 34 35 36 };
div1
250pts MaxMinTreeGame
题意:给定一棵N(2 <= N <= 50)的树,两人轮流进行游戏,每次可以删除一条边,然后选择保留其中一棵子树,直到仅剩下一个结点,游戏结束,每个结点都有一个权值,
A想要使得 最后结果尽量大,B想要使得结果尽量小,两人均按照最优方式进行,A先手,求A最终得到的最大值。
分析:所有度数为1的点的中权值最大值(M)即为结果,首先要证明所能获得的最大值不会超过M,因为N>=2的树中度数为1 的结点至少2个,
所以不论A先手时如何操作,剩下的树中,一定会保留下这些结点中的一个,B操作时选取即可,然后证明A先手能够保留权值最大的结点,这个是显然的。
代码:
1 const int maxn = 55; 2 int du[maxn]; 3 4 class MaxMinTreeGame { 5 public: 6 int findend(vector <int> edges, vector <int> costs) { 7 memset(du, 0, sizeof du); 8 int n = sz(edges) + 1; 9 for(int i = 0; i < sz(edges); i++) 10 du[i+1]++, du[edges[i]]++; 11 int ans = 0; 12 for(int i = 0; i < n; i++) 13 if(du[i] == 1) 14 ans = max(ans, costs[i]); 15 return ans; 16 } 17 18 19 };
500pts PairsOfStrings
题意:给定字符集合为前k个小写字母,定义字符集合上的长度为n的A,B字符串,若存在定义在集合上的C使得A+C=C+B,那么(A,B)记为一对,
现在问(n,k)能够确定的数目,结果MOD (int)1e9 + 7。
分析:首先应该知道B应该是A旋转后的字符串,定义字符串的最小周期长度,A = d^n/d,表示A由n/d个长度为d字符串(记为s)链接构成,A = s + s + ... s,
且不存在更小的长度为d' < d的字符串s',s'重复n/d'之后能够得到A。这样A旋转操作能够得到的不同字符串就为d。那么对于本题需要知道最小周期长度的为d字符串有多少个(num),
最终结果就是所有的d*num之和。显然d应该是n的因子,可能的情况是k^d种,然后这里会有存在重复的情况,例如n = 8, d = 4时,结果k^4中,会包含d = 2中情况,
s = aaaa,A = s+s = aaaaaaaa,显然A可以看做周期长度更小的s' = aa,A = s' + s' + s' + s',所以要把d的因子d'所对应的情况排除。n <= (int)1e9,因子最多1300+个,最后复杂度应该是
O(1300*1300)。
代码:
1 const int M = 1000000007; 2 3 class PairsOfStrings { 4 public: 5 int num[1500]; 6 int powmod(LL a, LL b, LL c) { 7 LL res = 1; 8 while(b) { 9 if(b&1) res = res*a%c; 10 a = a*a%c; 11 b >>= 1; 12 } 13 return res; 14 } 15 void addIt(int &x, int y) { 16 x = (x+y)%M; 17 if(x < 0) x += M; 18 } 19 void getDivisors(int n, vector<int> &div) { 20 div.clear(); 21 int m = (int)sqrt(n+.5); 22 for(int i = 1; i <= m; i++) 23 if(n%i == 0) { 24 div.pb(i); 25 if(n/i != i) 26 div.pb(n/i); 27 } 28 sort(div.begin(), div.end()); 29 } 30 vector<int> div; 31 int getNumber(int n, int k) { 32 33 getDivisors(n, div); 34 int ans = 0; 35 for(int i = 0; i < sz(div); i++) { 36 int x = div[i]; 37 num[i] = powmod(k, x, M); 38 for(int j = 0; j < i; j++) { 39 int y = div[j]; 40 if(x%y == 0) { 41 addIt(num[i], -num[j]); 42 } 43 } 44 addIt(ans, 1LL*num[i]*x%M); 45 } 46 return ans; 47 } 48 49 50 };
1000pts SumOfArrays
题意:A,B两个数组长度 n <= 100000,A[i],B[i] < 100000,将A,B中的数排列后,使得C[i] = A[i]+B[i]使得C[i]中出现过的数Y出现次数最大。
分析:又是FFT的应用,做法很奇特!分别统计A[i]和B[i]中数出线次数即cntA[A[i]],cntB[B[i]],然后考虑
解法的关键之处就是这里的转化,考虑min(cntA[p],cntB[q]) >= k,那么C[p+q]为(p,q,k)的组数,针对k分别考虑:
k >= 10,显然cntA[p],cntB[q] >= k的p和去不超过(int)1e4,暴力统计C[p+q]的复杂度不会超过(int)1e8,当然实际复杂度可能更低。
k < 10,
z[] = full of zeros For p = 0 ... 100000 { For each q = 0 ... 100000 { z[p + q] = z[p + q] + (x[p] * y[q]) } } for i = 0 ... 200000 { C[i] = C[i] + z[i] }
这里和大数的乘法十分相似,设立两个数组x[],y[],当x[p] = cntA[p] >= k,y[q] = cntB[q] >= k,剩下的部分利用FFT求出,z[p+q] += x[p]*y[q],由于FFT复杂度为O(MAX*log(MAX)),9次FFT是能够满足效率要求的。
代码:
1 const int maxn = (int)2e5 + 10; 2 const int LOW = 10; 3 4 int cntA[maxn], cntB[maxn]; 5 int A[maxn], B[maxn], C[maxn]; 6 bitset<maxn> a, b; 7 8 struct Complex { 9 double x, y; 10 Complex() {} 11 Complex(double x, double y):x(x), y(y) {} 12 }; 13 Complex operator + (const Complex &a, const Complex &b) { 14 Complex c; 15 c.x = a.x+b.x; 16 c.y = a.y+b.y; 17 return c; 18 } 19 Complex operator - (const Complex &a, const Complex &b) { 20 Complex c; 21 c.x = a.x-b.x; 22 c.y = a.y-b.y; 23 return c; 24 } 25 Complex operator * (const Complex &a, const Complex &b) { 26 Complex c; 27 c.x = a.x*b.x-a.y*b.y; 28 c.y = a.x*b.y+a.y*b.x; 29 return c; 30 } 31 32 inline void FFT(vector<Complex> &a, bool inverse) { 33 int n = a.size(); 34 for(int i = 0, j = 0; i < n; i++) { 35 if(j > i) 36 swap(a[i], a[j]); 37 int k = n; 38 while(j & (k>>=1)) j &= ~k; 39 j |= k; 40 } 41 double PI = inverse ? -pi : pi; 42 for(int step = 2; step <= n; step <<= 1) { 43 double alpha = 2*PI/step; 44 Complex wn(cos(alpha), sin(alpha)); 45 for(int k = 0; k < n; k += step) { 46 Complex w(1, 0); 47 for(int Ek = k; Ek < k+step/2; Ek++) { 48 int Ok = Ek + step/2; 49 Complex u = a[Ek]; 50 51 52 Complex t = a[Ok]*w; 53 a[Ok] = u-t; 54 a[Ek] = u+t; 55 w = w*wn; 56 } 57 } 58 } 59 if(inverse) 60 for(int i = 0; i < n; i++) 61 a[i].x = (a[i].x/n); 62 } 63 vector<int> operator * (const bitset<maxn> &v1, const bitset<maxn> &v2) { 64 int S1 = v1.size(), S2 = v2.size(); 65 int S = 2; 66 while(S < S1+S2) S <<= 1; 67 vector<Complex> a(S), b(S); 68 for(int i = 0; i < S; i++) 69 a[i].x = a[i].y = b[i].x = b[i].y = 0.0; 70 for(int i = 0; i < S1; i++) 71 a[i].x = v1[i]; 72 for(int i = 0; i < S2; i++) 73 b[i].x = v2[i]; 74 FFT(a, false); 75 FFT(b, false); 76 for(int i = 0; i < S; i++) 77 a[i] = a[i] * b[i]; 78 FFT(a, true); 79 vector<int> res(maxn, 0); 80 for(int i = 0; i < maxn; i++) 81 res[i] = round(a[i].x); 82 return res; 83 } 84 85 class SumOfArrays { 86 public: 87 void gen(int A[], vector<int> seed, int n) { 88 A[0] = seed[0]; 89 A[1] = seed[1]; 90 for(int i = 2; i < n; i++) 91 A[i] = (1LL * A[i-1] * seed[2] + 1LL* A[i-2] * seed[3] + seed[4]) % seed[5]; 92 // for(int i = 0; i < n; i++) 93 // printf("%d ", A[i]); 94 // puts(""); 95 } 96 char ans[100]; 97 98 string findbestpair(int n, vector <int> Aseed, vector <int> Bseed) { 99 gen(A, Aseed, n); 100 gen(B, Bseed, n); 101 // memset(cntA, 0, sizeof cntA); 102 memset(cntB, 0, sizeof cntB); 103 memset(C, 0, sizeof C); 104 105 for(int i = 0; i < n; i++) { 106 cntA[A[i]]++; 107 cntB[B[i]]++; 108 } 109 110 vector<int> bigA, bigB; 111 for(int i = 0; i < maxn; i++) { 112 if(cntA[i] >= LOW) 113 bigA.pb(i); 114 if(cntB[i] >= LOW) 115 bigB.pb(i); 116 } 117 118 for(int p: bigA) for(int q: bigB) { 119 C[p+q] += min(cntA[p], cntB[q]) - LOW + 1; 120 } 121 vector<int> c(maxn); 122 for(int k = 1; k < 10; k++) { 123 a.reset(); 124 b.reset(); 125 for(int i = 0; i < maxn; i++) { 126 if(cntA[i] >= k) 127 a[i] = 1; 128 if(cntB[i] >= k) 129 b[i] = 1; 130 } 131 132 c = a*b; 133 for(int i = 0; i < maxn; i++) 134 C[i] += c[i]; 135 // for(int i = 1; i <= 4; i++) 136 // cout << C[i] << ' '; 137 // cout << endl; 138 } 139 140 int X = -1, Y = 0; 141 for(int i = 0; i < maxn; i++) 142 if(C[i] >= X) { 143 X = C[i]; 144 Y = i; 145 } 146 147 sprintf(ans, "%d %d", X, Y); 148 // TL 149 return ans; 150 } 151 152 153 };