2018.10.06模拟总结

看是老家的中学出的题目,会不会可爱一点呢?

嗯,真可爱,可爱的我两道题文件都错了。

因为最近我把文件输入输出改成#ifndef和#endif的形式了,然后放在了一个函数里,结果敲板子的时候忘调用了……

鬼知道为啥我T1敲了上去。

 

T1 rps

  期望得分:20

  实际得分:40

  看了看题,发现这其实构成了一棵满二叉树,但这好像并没有什么用。然后就开始想暴力,可惜的是暴力都不知道怎么写。随便看了看上个厕所,突然发现只要最后的胜者确定了,这个序列就是唯一的,仅仅是没排好序。于是我就开始枚举胜者,再分治判断,然后我就不知怎么的开始犯zz了:如果合法,就直接排序输出,然后break了!我当时不知怎么想的,认为合法的序列就一个,然后又担心时间复杂度,决定找一个就完事……更有乐的是,我排序的时候又怕时间不够,只比较了两个序列的前4个字符……

  所以估分的时候就只敢保证送的那20分。

  正解其实和我的思路一样:枚举最终胜者,判定是否合法,如果合法,就排完序存下来,最后从得到的三个序列中取字典序最小的。排序就像归并排序,所以复杂度O(3 * nlogn)。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = (1 << 20) + 5;
 21 const int base = 100;
 22 inline ll read()
 23 {
 24     ll ans = 0;
 25     char ch = getchar(), last = ' ';
 26     while(!isdigit(ch)) {last = ch; ch = getchar();}
 27     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 28     if(last == '-') ans = -ans;
 29     return ans;
 30 }
 31 inline void write(ll x)
 32 {
 33     if(x < 0) x = -x, putchar('-');
 34     if(x >= 10) write(x / 10);
 35     putchar(x % 10 + '0');
 36 }
 37 void MYFILE()
 38 {
 39 #ifndef mrclr
 40   freopen("rps.in", "r", stdin);
 41   freopen("rps.out", "w", stdout);
 42 #endif
 43 }
 44 
 45 int r, p, s, n;
 46 const char c[3] = {'P', 'R', 'S'};
 47 int num[3], a[maxn], ans[3][maxn];
 48 
 49 bool judge(int L, int R, int x)
 50 {
 51     if(L == R)
 52     {
 53         if(--num[x] < 0) return 0;
 54         a[L] = x; return 1;
 55     }
 56     int mid = (L + R) >> 1;
 57     return judge(L, mid, x) && judge(mid + 1, R, ++x > 2 ? 0 : x);
 58 }
 59 void rotate(int L, int R)        //归并排序 
 60 {
 61     if(L == R) return;
 62     int mid = (L + R) >> 1;
 63     rotate(L, mid); rotate(mid + 1, R);
 64     bool flg = 0;
 65     for(int i = L, j = mid + 1; i <= mid; ++i, ++j)
 66         if(a[i] > a[j]) {flg = 1; break;}
 67     if(flg) for(int i = L, j = mid + 1; i <= mid; ++i, ++j)
 68         swap(a[i], a[j]);
 69 }
 70 
 71 void init()
 72 {
 73     num[0] = p; num[1] = r; num[2] = s;
 74 }
 75 
 76 void work(int x)
 77 {
 78     init();
 79     if(judge(1, n, x))
 80     { 
 81         rotate(1, n);
 82         for(int i = 1; i <= n; ++i) ans[x][i] = a[i];
 83     }
 84 }
 85 
 86 int main()
 87 {
 88   MYFILE();
 89   r = read(), p = read(), s = read();
 90   n = r + p + s;
 91   for(int i = 0; i < 3; ++i) ans[i][1] = 'Z';
 92   for(int i = 0; i < 3; ++i) work(i);
 93   for(int i = 1; i <= n; ++i)        //一下繁琐的代码其实就是在找三者中字典序最小的…… 
 94   {
 95     int Min = ans[0][i], pos = 0, pos2 = ans[0][i];
 96       for(int j = 1; j < 3; ++j)
 97       {
 98         if(ans[j][i] < Min) Min = ans[j][i], pos = j;
 99         if(ans[j][i] != pos2) pos2 = -1;
100       } 
101     if(pos2 == -1)
102       {
103           for(int j = 1; j <= n; ++j) putchar(c[ans[pos][j]]);
104           enter; return 0;
105       }
106   }
107   printf("IMPOSSIBLE
");
108   return 0;
109 }
View Code

T2 vote

  期望得分:30

  实际得分:0(30)

  0分就是文件的事了……加上后是30.

  一看就不会呀!管他什么期望dp,上去先捡了10分,然后对于1,2测试点,二进制暴力枚举,复杂度O(C(k, n) * C(k / 2, k) * n),代码里面我预处理了从n中取k个的所有情况,所以实际复杂度能更低一点。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const int maxn = 18;
21 inline ll read()
22 {
23     ll ans = 0;
24     char ch = getchar(), last = ' ';
25     while(!isdigit(ch)) {last = ch; ch = getchar();}
26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
27     if(last == '-') ans = -ans;
28     return ans;
29 }
30 inline void write(ll x)
31 {
32     if(x < 0) x = -x, putchar('-');
33     if(x >= 10) write(x / 10);
34     putchar(x % 10 + '0');
35 }
36 void MYFILE()
37 {
38 #ifndef mrclr
39   freopen("vote.in", "r", stdin);
40   freopen("vote.out", "w", stdout);
41 #endif
42 }
43 
44 int n, k;
45 db a[maxn];
46 
47 int s[1 << maxn], cnt = 0;
48 int s2[1 << maxn], cnt2 = 0;
49 void init()
50 {
51   for(int i = 0; i < (1 << n); ++i)
52     {
53       int x = 0, tp = i;
54       while(tp)
55     {
56       if(tp & 1) x++;
57       tp >>= 1;
58     }
59       if(x == k) s[++cnt] = i;
60       if(x == (k >> 1)) s2[++cnt2] = i;
61     }
62 }
63 
64 db ans = 0;
65 
66 int main()
67 {
68   MYFILE();        //就少了这个! 
69   n = read(); k = read();
70   for(int i = 1; i <= n; ++i) scanf("%lf", &a[i]);
71   if(n == 2) {printf("%.8lf
", a[1] * (1 - a[2]) + (1 - a[1]) * a[2]); return 0;}
72   if(n > 16) {printf("0.500000000
"); return 0;}
73   init();
74   for(int i = 1; i <= cnt; ++i)
75     {
76       db Max = 0;
77       for(int j = 1; j <= cnt2; ++j) if((s[i] & s2[j]) == s2[j]) 
78     {
79       db x = 1;
80       for(int h = 1; h <= n; ++h)
81         {
82           if((1 << (h - 1)) & s2[j]) x *= (1 - a[h]);
83           else if((1 << (h - 1)) & s[i]) x *= a[h];
84         }
85       Max += x;
86     }
87       ans = max(ans, Max);
88     }
89   printf("%.8lf
", ans);
90   return 0;
91 }
View Code

  正解其实还是比较好理解的……

  有一个结论,就是先排个序,选出的k个数一定是一个前缀和一个后缀,证明放在最后。

  接下来就比较好想了:我们先做一个n = k的情况:令dp[i][j]表示在选了的长度为 i 的前缀中,用 j 个人投了“好票”的概率,那么dp[i][j] = a[i] * dp[i - 1][j - 1] + (1 - a[i]) * dp[i - 1][j]。

  最终输出dp[n][n / 2]即可。

  对于k != n的情况,我们既然已经知道了肯定是选一个前缀和后缀,那么就dp两次:令pre[i][j]表示在选了的前 i 个人中有 j 个投了“好票”的概率,suf[i][j]表示在选了的后 i 个人中有 j 个人头好票的概率。转移方程和上面几乎一样,就不讲了。

  维护好了这俩,我们再O(n2)枚举一下就行:枚举选了前 x 个人,所以后缀就是k - x个,再枚举在x个人中有h个人投“好票”。然后维护最大值就行了。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const int maxn = 2e3 + 5;
21 inline ll read()
22 {
23     ll ans = 0;
24     char ch = getchar(), last = ' ';
25     while(!isdigit(ch)) {last = ch; ch = getchar();}
26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
27     if(last == '-') ans = -ans;
28     return ans;
29 }
30 inline void write(ll x)
31 {
32     if(x < 0) x = -x, putchar('-');
33     if(x >= 10) write(x / 10);
34     putchar(x % 10 + '0');
35 }
36 void MYFILE()
37 {
38 #ifndef mrclr
39     freopen("vote.in", "r", stdin);
40     freopen("vote.out", "w", stdout);
41 #endif
42 }
43 
44 int n, k;
45 db a[maxn];
46 db pre[maxn][maxn], suf[maxn][maxn];
47 
48 int main()
49 {
50     MYFILE();
51     n = read(); k = read();
52     for(int i = 1; i <= n; ++i) scanf("%lf", &a[i]);
53     sort(a + 1, a + n + 1);
54     pre[0][0] = 1;
55     for(int i = 1; i <= n; ++i)
56     {
57         for(int j = 1; j <= i; ++j)
58             pre[i][j] = a[i] * pre[i - 1][j - 1] + (1 - a[i]) * pre[i - 1][j];
59         pre[i][0] = (1 - a[i]) * pre[i - 1][0];
60     }
61     suf[n + 1][0] = 1;
62     for(int i = n; i; --i)
63     {
64         for(int j = 1; j <= n - i + 1; ++j)
65             suf[i][j] = a[i] * suf[i + 1][j - 1] + (1 - a[i]) * suf[i + 1][j];
66         suf[i][0] = (1 - a[i]) * suf[i + 1][0];
67     }
68     db ans = 0;
69     for(int L = 0; L <= k; ++L)        //pre : 0 ~ k
70     {
71         int R = n + 1 + L - k; 
72         db Max = 0;
73         for(int i = 0; i <= (k >> 1); ++i) Max += pre[L][i] * suf[R][(k >> 1) - i];
74         ans = max(ans, Max);
75     }
76     printf("%.8lf
", ans);
77     return 0;
78 }
View Code

证明来啦:

  首先是感性理解:假设有两个人,一个人投“好票”的概率很大,一个人投“好票”的概率很小,那么他们两人投平票的概率就很大。完了。

  数学证明(Ssy大佬讲的,巨):我们假设[1, L]和[R, n]已经都选了,也就是说我们要证明下一个数应该在L +1处或是R - 1处取到。

  dp方程可以写出来:dp[i][j] = a[i] * dp[L][j - 1] + (1 - a[i]) * dp[L][j]      i∈[L + 1, R - 1]

  因为对于dp过的结果不会改变,不妨令A = dp[L][j - 1], B = dp[L][j],

  则dp[i][j] = A * a[i] + B * (1 - a[i]) = (A - B) * a[i] + B

  可见这是一个一次函数,那么自然在端点处取得最值。

T3 factory

  期望得分:10

  实际得分:0(20)

  我再也不会忘写文件了。

  看者数据范围我就觉得是最小割,结果图怎么也建不出来,最后搞了一个只能过样例的错误建图方法,自然是gg了。

  题解没看懂,大概是用二分图证明任意一个极大匹配都是完美匹配,然后状压dp求解。

今天是真有点惨,希望明天能扳回来点儿……

原文地址:https://www.cnblogs.com/mrclr/p/9747676.html