SRM470 - SRM474(1-250pt,500pt)(471-500pt为最短路,474-500pt未做)

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 };
View Code

 

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 };
View Code

 

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 };
View Code

 

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 };
View Code

 

 

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 };
View Code

 

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 };
View Code 

 

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 };
View Code

 

DIV1 500pt

这场SRM带“member”,所以没有题解。不会。 

 

posted @ 2013-09-09 12:52  Plumrain  阅读(256)  评论(0编辑  收藏  举报