SRM 470
DIV1 250pt
题意:有n个房间排成一排,相邻两个房间之间有一扇关闭着的门(共n-1扇),每个门上都标有‘A’-‘P’的大写字母。给定一个数n,表示第n个房间。有两个人John和Gogo,两人轮流按下面的规则选择一个大写字母(‘A’-‘P’),每选择一次字母,标有该字母的门就打开了。
某次选择之后:若所有门全部打开,则平手游戏结束,输出0;n房间之前的门全部打开,则John胜游戏结束,输出两人总共选择了的颜色的数量;n房间后的门全部打开,则Gogo胜游戏结束,输出-1 × 两人总共选择了的颜色的数量。否则游戏继续。
选择颜色的规则:
1、若选择某个颜色后,无论对方如何选择,都可以使自己获得胜利,则选择该颜色。若有多个这种颜色,则选择可以使游戏结束时总共选择颜色数最少的那种颜色。
2、在1不成立的情况下,若选择某中颜色后,无论对方如何选择,都可以与对手打成平局,则选择该颜色。若有多个这种颜色,处理方法同上。
3、在1,2均不成立的情况下,选择能够使得游戏结束时选择颜色种数最多的颜色。
score: 0
解法:直接看代码把- -,感觉能看懂的,不复杂
tag: think

1 /* 2 * Author: plum rain 3 */ 4 #line 10 "DoorsGame.cpp" 5 #include <iostream> 6 #include <cstdio> 7 #include <sstream> 8 #include <cstring> 9 #include <vector> 10 11 using namespace std; 12 13 #define CLR(x) memset(x, 0, sizeof(x)) 14 #define PB push_back 15 #define SZ(v) ((int)(v).size()) 16 #define out(x) cout<<#x<<":"<<(x)<<endl 17 #define tst(a) cout<<#a<<endl 18 19 string a, b; 20 bool hav1[20], hav2[20]; 21 22 int gao() 23 { 24 int an = SZ (a), bn = SZ (b); 25 CLR (hav1); CLR (hav2); 26 int num1 = 0, num2 = 0; 27 for (int i = 0; i < an; ++ i){ 28 if (hav1[a[i]-'A']) continue; 29 hav1[a[i]-'A'] = 1; 30 num1 ++; 31 } 32 for (int i = 0; i < bn; ++ i){ 33 if (hav2[b[i]-'A']) continue; 34 hav2[b[i]-'A'] = 1; 35 num2 ++; 36 } 37 38 int dif = 0; 39 for (int i = 0; i <= 'P'; ++ i) 40 if (hav1[i] && hav2[i]) 41 ++ dif; 42 43 if (num1 == num2){ 44 if (!dif) return 2*num1 - 1; 45 return 0; 46 } 47 if (num1 < num2){ 48 if (dif <= num2 - num1) return 2*num1 - 1; 49 return 0; 50 } 51 if (num1 > num2){ 52 if (dif < num1 - num2) return -2 * num2; 53 return 0; 54 } 55 } 56 57 class DoorsGame 58 { 59 public: 60 int determineOutcome(string tmp, int t){ 61 a.clear(); b.clear(); 62 int n = SZ (tmp); 63 for (int i = 0; i < n; ++ i){ 64 if (i < t) a.PB (tmp[i]); 65 else b.PB(tmp[i]); 66 } 67 return (gao()); 68 } 69 };
DIV1 500pt
题意:给出一个矩形,在矩形上下两边分别标记出n个点,并且已经在上下两边所标记的点中连出若干条线段(每个点只能在一条线段上)。给定n和所连线段。每次随机在矩形上边和下边取一个没连线段的点(取到没点概率相同),将它们相连,直到所有点都被连接。求最终所形成交点数的期望(多点共线算C(k, 2)个交点)。
解法:用l[n]表示线段,a[i]表示线段l[i]在矩形上边的端点,b[i]表示线段l[i]在矩形下边的端点。称题目中给出线段的端点为不可连接点,其余矩形边上的点为可连接点。用E(a[i], a[j])表示由a[i], a[j]分别与矩形下边的两点相连,形成交点的期望。题目中给出的线段的数量为size。
若a[i],a[j]均为可连接点:该种情况下,(a[i], a[j])一共有(n-size) * (n-size-1) / 2对,每对产生交点的概率是0.5,所以这种情况期望之和为sum{E(a[i], a[j])} = (n-size) * (n-siez-1) / 4。
若a[i],a[j]均为不可连接点:暴力算出交点个数即可,概率为1,所以该情况下交点期望之和即为交点数。
若a[i],a[j]中一个为可连接点,另一个不可连接点:该种情况下,对每条题目给定边分别分析,期望之和即为该种情况的期望。对于某条题目给定边l[i],在矩形上边在a[i]左侧有tl个可连接点,右侧有tr个可连接点,在矩形下边b[i]左侧有bl个可连接点,b[i]右侧有br个可连接点,则期望为Ei = tl*br/(tl+tr) + tr*bl/(tl+tr)。所以,该种情况的期望和为sum{Ei}。
三个部分相加,此题得解。
Ps:此题解法真的很巧秒。
tag:math, expectation, think, good

1 /* 2 * Author: plum rain 3 */ 4 #line 10 "DrawingLines.cpp" 5 #include <iostream> 6 #include <cstdio> 7 #include <sstream> 8 #include <cstring> 9 #include <algorithm> 10 #include <vector> 11 12 using namespace std; 13 14 #define CLR(x) memset(x, 0, sizeof(x)) 15 #define PB push_back 16 #define SZ(v) ((int)(v).size()) 17 18 const int N = 10000; 19 20 struct dot{ 21 int first, second; 22 }; 23 24 dot a[N+1]; 25 int top[N+1], bot[N+1]; 26 27 bool cmp(dot a, dot b) 28 { 29 return a.first < b.first; 30 } 31 32 double gao(int size, int n) 33 { 34 double ret = (n-size) * (n-size-1.0) / 4.0; 35 36 for (int i = 0; i < size; ++ i){ 37 for (int j = i+1; j < size; ++ j) 38 if (a[j].second < a[i].second) ret += 1.0; 39 40 int tl = top[a[i].first], tr = n - size - tl; 41 int bl = bot[a[i].second], br = n - size - bl; 42 ret += tl*br*1.0 / (n-size-0.0) + tr*bl*1.0 / (n-size-0.0); 43 } 44 return ret; 45 } 46 47 class DrawingLines 48 { 49 public: 50 double countLineCrossings(int n, vector <int> s, vector <int> e){ 51 int size = SZ (s); 52 for (int i = 0; i < size; ++ i){ 53 a[i].first = s[i]; 54 a[i].second = e[i]; 55 } 56 sort (a, a+size, cmp); 57 58 CLR (top); CLR (bot); 59 sort (s.begin(), s.end()); 60 for (int i = 0; i < size; ++ i) 61 top[s[i]] = s[i] - i - 1; 62 sort (e.begin(), e.end()); 63 for (int j = 0; j < size; ++ j) 64 bot[e[j]] = e[j] - j - 1; 65 66 return (gao(size, n)); 67 } 68 };
SRM 471
DIV1 250pt
题意:对于一个质数p,定义其“p的质数长度为d“ 等价于”集合{p / (2^i) | i = 0, 1, 2...d-1}中全部为质数“。给定数n和d,找到最大的数m使得m <= n且m的质数长度至少为d。
解法:水题。只需要一个素数表和一个二分查找,然后暴力就好了。
score: 128.03
tag:math
Ps:如果还用以前自己的素数打表模板而不是换成刘汝佳的,感觉就要超时了- -

1 /* 2 * Author: plum rain 3 * score : 128.03 4 */ 5 #line 10 "PrimeSequences.cpp" 6 #include <iostream> 7 #include <cstdio> 8 #include <sstream> 9 #include <cstring> 10 #include <cmath> 11 #include <vector> 12 #include <fstream> 13 #include <numeric> 14 #include <iomanip> 15 #include <stdexcept> 16 #include <functional> 17 #include <ctime> 18 19 using namespace std; 20 21 #define CLR(x) memset(x, 0, sizeof(x)) 22 23 const int N = 10000000; 24 bool vis[N+1]; 25 int prm[N+1]; 26 27 void sieve(int n) 28 { 29 int m = (int)sqrt(n+0.5); 30 CLR (vis); 31 for (int i = 2; i <= m; ++ i) if (!vis[i]) 32 for (int j = i*i; j <= n; j += i) vis[j] = 1; 33 } 34 35 int primes(int n) 36 { 37 sieve(n); 38 int ret = 0; 39 for (int i = 2; i <= n; ++ i) 40 if (!vis[i]) prm[ret++] = i; 41 return ret; 42 } 43 44 int bin_search(int n, int all) 45 { 46 int l = 0, r = all-1; 47 while (l <= r){ 48 int mid = (l + r) >> 1; 49 if (prm[mid] < n) l = mid + 1; 50 else r = mid - 1; 51 } 52 return l; 53 } 54 55 bool gao(int n, int d) 56 { 57 if (vis[n]) return false; 58 int times = 1; 59 while (times < d){ 60 n >>= 1; 61 if (vis[n] == 1 || n == 1) return false; 62 ++ times; 63 } 64 return true; 65 } 66 67 class PrimeSequences 68 { 69 public: 70 int getLargestGenerator(int n, int d){ 71 int all = primes(N+1000); 72 int pos = bin_search(n, all); 73 while (prm[pos+1] < n) 74 ++ pos; 75 while (prm[pos] > n) 76 -- pos; 77 while (pos >= 0){ 78 if (gao(prm[pos], d)) return prm[pos]; 79 -- pos; 80 } 81 return -1; 82 } 83 };
DIV1 500pt
题意: 给定N个地点,标号0-(N-1),N个地点之间有一些路,每条路都有长度。问从0号地点走到N-1号地点,最短且满足以下条件的路的长度:设路径为P[0], P[1], P[2]....P[k](其中P[0] = 0),T[m]为从P[m]到P[m+1]的长度,则不存在0 <= i <= j < k,使得T[i] + T[i+1] +...T[j]为13的倍数。
解法:如果没有限制条件,就是一道很简单的最短路的题。而限制条件等价于不存在0 <= i <= j < k,使得T[-1]+T[0]+T[1]+T[2]+T[i-1]与T[0]+T[1]+T[2]+...T[j]同余(其中T[-1]视为0)。这样,12位的状态压缩就解决了问题。
Ps:好不容易想出了500pt,可是,由于我没学图论(- -),这道题还是不能写- -。
tag: math, 最短路, 未做
SRM 472
DIV1 250pt
题意:A和B轮流做游戏。给定一个数m(1<= m <= 10^9),每人每轮必须使m = m - 4^k(k是使得4^k <= m的任意非负整数),使m = 0的人胜利。给定m,A先,都使用最优策略,问谁能获胜。
解法:手算模拟,找出规律为:令t = m % 5,当t为1, 3, 4的时候A胜,否则B胜。通过数学归纳法验证:
引理:if (k为偶数) (4^k) = 1(mod 5),if (k为奇数) (4^k) = 4(mod 5)。(易证)
当m = 1, 2, 3, 4, 5时符合规律。
设当m <= 5*n时均符合规律,则当5*n < m < 5*(n+1)时:
当m = 5*n + 1,先手使m = m - 1,则m = 5*n,由归纳假设知m=5*n为先手必败,所以m = 5*n+1为先手必胜(即A胜)。
当m = 5*n + 2,先手m = m - 4,则m = 5*n - 2,由归纳假设知m = 5*n-2为先手必败,所以m = 5*n-2为先手必胜。
当m = 5*n + 3,引理知,对于任意k,均有(m - 4^k) = 2或4(mod 5),由归纳假设知他们均为先手必胜,所以m = 5*n + 3先手必败。
当m = 5*n + 4,先手使m = m - 1则必胜。
当m = 5*n + 5,同5*n+3理,易证为必败。
DIV1 600pt
题意:给定数字1-n的两个排列(可能相同),记为a[n],b[n](0~n-1)。新构造一个数列k[n]使得k[i] = a[i]或则b[i],则所有k[n]的不同排列有多少种。比如,a[] = {1,3,2},b[] = {1,2,3},则k[] = {1,2,2}或{1,2,3}或{1,3,2}或{1,3,3},则所有k[n]的不同排列有:123,132,213,231,312,321,122,212,221,133,313,331,共12种。
解法:首先,将a[n]置换到b[n]记为置换A,将置换A有若干个循环,对每个循环单独分析,分析每个循环有多少种排列。对每个循环,设其长度为m,其中含有的每个数字都只可能出现0,1,2次,而且,注意到,每选择一个数出现两次,则注定同时会有一个数出现0次。所以枚举出现两次的数的个数为k时该循环能产生多少个排列,k = 0 -> (m/2),则当k=0,排列个数为A[m][m],否则为C[m][2*k] * 2 * A[m][m]/(2^k)。注意到,选择k个数出现0次和k个数出现2次只能用C[m][2*k]*2,这也是我没想到的点之一,不信可以试试别的方法。
求出每个循环的排列数以后,就只需要在长度为n的空序列上找位置就好了。
tag:math, counting, good
score: 0

1 /* 2 * Author: plum rain 3 * score : 0 4 */ 5 #line 11 "TwoSidedCards.cpp" 6 #include <sstream> 7 #include <stdexcept> 8 #include <functional> 9 #include <iomanip> 10 #include <numeric> 11 #include <fstream> 12 #include <cctype> 13 #include <iostream> 14 #include <cstdio> 15 #include <vector> 16 #include <cstring> 17 #include <map> 18 19 using namespace std; 20 21 #define CLR(x) memset(x, 0, sizeof(x)) 22 #define SZ(v) ((int)(v).size()) 23 #define out(x) cout<<#x<<":"<<(x)<<endl 24 #define tst(a) cout<<#a<<endl 25 26 typedef long long int64; 27 28 const int mod = 1000000007; 29 30 map<int, int> mp; 31 int64 A[55][55]; 32 int64 C[55][55]; 33 34 int pow_mod(int64 p, int64 n) 35 { 36 int64 ret = 1; 37 while (n > 0){ 38 if (n & 1) ret = (ret * p) % mod; 39 n >>= 1; 40 p = (p * p) % mod; 41 } 42 return (int)ret; 43 } 44 45 void C_init() 46 { 47 C[0][0] = 1; 48 for (int i = 1; i <= 50; ++ i){ 49 C[i][0] = C[i][i] = 1; 50 for (int j = 1; j < i; ++ j) 51 C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod; 52 } 53 } 54 55 void A_init() 56 { 57 A[0][0] = 1; 58 A[1][1] = 1; A[1][0] = 1; 59 for (int i = 2; i <= 50; ++ i){ 60 A[i][0] = 1; 61 for (int j = 1; j <= i; ++ j) 62 A[i][j] = (A[i][j-1] * (i-j+1)) % mod; 63 } 64 } 65 66 int64 gao2(int n) 67 { 68 if (n == 1) return 1; 69 70 int64 ret = 0; 71 for (int i = 0; 2*i <= n; ++ i){ 72 int64 tmp; 73 if (!i) tmp = 1; 74 else tmp = C[n][2*i] * 2; 75 tmp = (tmp * A[n][n]) % mod; 76 tmp = (tmp * pow_mod(pow_mod(2, i), mod-2)) % mod; 77 ret = (tmp + ret) % mod; 78 } 79 return ret; 80 } 81 82 int gao(int n) 83 { 84 int64 ret = 1; 85 int tot = n; 86 for (int i = 1; i <= n; ++ i){ 87 int tmp = 0, pos = i; 88 89 while (mp[pos] != -1){ 90 ++ tmp; 91 int x = pos; 92 pos = mp[pos]; 93 mp[x] = -1; 94 } 95 if (tmp){ 96 int64 xxx = gao2(tmp); 97 ret = (((C[tot][tmp] * xxx) % mod) * ret) % mod; 98 } 99 tot -= tmp; 100 } 101 return ret; 102 } 103 104 class TwoSidedCards 105 { 106 public: 107 int theCount(vector <int> t, vector <int> h){ 108 C_init(); 109 A_init(); 110 111 mp.clear(); 112 int n = SZ (t); 113 for (int i = 0; i < n; ++ i) 114 mp[t[i]] = h[i]; 115 116 return gao(n); 117 } 118 };
SRM 473
DIV1 250pt
题意:给定一个字符串作为机器人的行走指令。字符串(最多50个字母)含三种字母——S向前走一步,L向左转,R向右转。问一直重复,无限次地进行这个字符串指令,问是否存在数R使得以初始点为圆心,以R为半径的圆能够包含所有机器人走过的地方。
解法:我的解法是,暴力让机器人走10000次,判断点的位置到初始点的距离。题解的方法很好懂很好做的,水题我就不多写了...
score: 0

1 /* 2 * Author: plum rain 3 * score : 0 4 * for better method: http://apps.topcoder.com/wiki/display/tc/SRM+473 5 */ 6 #line 11 "SequenceOfCommands.cpp" 7 #include <sstream> 8 #include <stdexcept> 9 #include <functional> 10 #include <iomanip> 11 #include <numeric> 12 #include <fstream> 13 #include <cctype> 14 #include <iostream> 15 #include <cstdio> 16 #include <vector> 17 #include <cmath> 18 19 20 using namespace std; 21 22 #define SZ(v) ((int)(v).size()) 23 24 typedef vector<string> VS; 25 typedef long long int64; 26 27 VS c; 28 // 0 29 //1 3 30 // 2 31 32 void move (int& x, int& y, int& d) 33 { 34 int size = SZ (c); 35 for (int i = 0; i < size; ++ i){ 36 int n = SZ (c[i]); 37 for (int j = 0; j < n; ++ j){ 38 if (c[i][j] == 'L') d = (d-1+4) % 4; 39 if (c[i][j] == 'R') d = (d+1) % 4; 40 41 if (c[i][j] == 'S'){ 42 if (d == 0) ++ y; 43 if (d == 1) -- x; 44 if (d == 2) -- y; 45 if (d == 3) ++ x; 46 } 47 } 48 } 49 } 50 51 class SequenceOfCommands 52 { 53 public: 54 string whatHappens(vector <string> com){ 55 c.clear(); c = com; 56 int x = 0, y = 0, d = 0; 57 for (int i = 0; i < 10000; i ++) 58 move (x, y, d); 59 double dis = (int)sqrt((int64)x*x + (int64)y*y + 0.0); 60 if (dis < 1000.0) return "bounded"; 61 return "unbounded"; 62 } 63 };
div1 500pt
题意:在圆上有n个点按顺时针方向标记为0, 1, 2....(n-1),将其中的rnum个涂红。给定a,b,c,for i = 0 to (rnum-1),将(a*i^2 + b*i + c) mod n之后的第一个没涂红的点涂红。最终,求完全由红点组成的直角三角形的数量。如n = 4, rnum = 3, a = 4, b = 4, c = 2,则依次涂红点2, 3, 0。
a, b, c, n <= 10^6, rnum <= 10^5, 且rnum <= n。
解法:其实,这道看上去的做法很简单,只需要按题意求出哪些点为红点,然后只需要用到圆直径所有对的角为直角这一性质就能轻松解决。但是,注意到这几个字“之后的第一个没涂红的点涂红”,导致暴力的时间复杂度为O(n^2),所以要用比较机智的hash来解决问题。hash方法见代码,很好懂。
tag:hash
score: 0
Ps:这实在是这几场里最水的500了,我虽然提交了多次(循环变量i爆int和TLE的问题),但最终也能在没看题解的情况下解出这道题(^_^)。

1 /* 2 * Author: plum rain 3 * score : 0 4 */ 5 #line 11 "RightTriangle.cpp" 6 #include <sstream> 7 #include <stdexcept> 8 #include <functional> 9 #include <iomanip> 10 #include <numeric> 11 #include <fstream> 12 #include <cctype> 13 #include <iostream> 14 #include <cstdio> 15 #include <vector> 16 #include <cstring> 17 //#include <string> 18 19 using namespace std; 20 21 #define out(x) cout<<#x<<":"<<(x)<<endl 22 #define tst(a) cout<<#a<<endl 23 24 typedef vector<int> VI; 25 typedef long long int64; 26 27 int cnt[1000500]; 28 bool has[1000500]; 29 30 class RightTriangle 31 { 32 public: 33 long long triangleCount(int n, int rnum, int aa, int bb, int cc){ 34 if (n & 1 || rnum <= 2) return 0; 35 int dif = n / 2; 36 37 int64 a = (int64)aa, b = (int64)bb, c = (int64)cc; 38 a %= n; b %= n; c %= n; 39 memset(has, 0, sizeof (has)); 40 memset(cnt, 0, sizeof (cnt)); 41 for (int64 i = 0; i < rnum; ++ i){ 42 int64 tmp = (i*i) % n; 43 tmp = (tmp * a) % n; 44 tmp = (tmp + (b * i) % n) % n; 45 tmp = (tmp + c) % n; 46 ++ cnt[tmp]; 47 } 48 49 int num = 0, i = 0; 50 while (num < rnum){ 51 if (i == 0) cnt[i] += cnt[n-1]; 52 else cnt[i] += cnt[i-1]; 53 54 if (cnt[i] && !has[i]){ 55 ++ num; 56 has[i] = 1; 57 -- cnt[i]; 58 } 59 ++ i; 60 if (i == n) i = 0; 61 } 62 63 int64 ret = 0; 64 for (int i = 0; i < dif; ++ i){ 65 if (has[i] && has[i+dif]) 66 ret ++; 67 } 68 return ret * (int64)(rnum - 2); 69 } 70 };
SRM 474
DIV1 250pt
题意:在N维空间(x1, x2,...., xn)中移动某个点,每次移动可以使某一维的坐标加1或者减1,一共移动m次。(1<= N <= 10^9,1 <= m <= 50)
解法:N这么大完全是吓人的,因为最多50次操作,每次操作最多在某一维中进行,所以直接暴力记录50维即可。
tag:water
score: 155.33

1 /* 2 * Author: plum rain 3 * score : 155.33 4 */ 5 #line 11 "RouteIntersection.cpp" 6 #include <sstream> 7 #include <stdexcept> 8 #include <functional> 9 #include <iomanip> 10 #include <numeric> 11 #include <fstream> 12 #include <cctype> 13 #include <iostream> 14 #include <cstdio> 15 #include <vector> 16 #include <cstring> 17 18 using namespace std; 19 20 #define CLR(x) memset(x, 0, sizeof(x)) 21 #define SZ(v) ((int)(v).size()) 22 #define out(x) cout<<#x<<":"<<(x)<<endl 23 #define tst(a) cout<<#a<<endl 24 25 struct pos{ 26 int a[55]; 27 void clr(){ 28 CLR (a); 29 } 30 bool operator == (pos b){ 31 for (int i = 0; i < 55; ++ i) 32 if (a[i] != b.a[i]) return false; 33 return true; 34 } 35 }; 36 pos p[55]; 37 int cnt[55]; 38 39 class RouteIntersection 40 { 41 public: 42 string isValid(int N, vector <int> c, string m){ 43 for (int i = 0; i < 55; ++ i) 44 p[i].clr(); 45 CLR (cnt); 46 int num = 0; 47 48 int n = SZ (c); 49 for (int i = 0; i < n; ++ i){ 50 int flag = -1; 51 for (int j = 0; j < num; ++ j) 52 if (c[i] == cnt[j]) flag = j; 53 if (flag == -1) 54 cnt[num] = c[i], flag = num ++; 55 56 if (i) p[i] = p[i-1]; 57 if (m[i] == '+') ++ p[i].a[flag]; 58 if (m[i] == '-') -- p[i].a[flag]; 59 } 60 61 ++ n; 62 for (int i = 0; i < n; ++ i) 63 for (int j = i+1; j < n; ++ j) 64 if (p[i] == p[j]) return "NOT VALID"; 65 return "VALID"; 66 } 67 };
DIV1 500pt
这场SRM带“member”,所以没有题解。不会。