[bzoj\lydsy\大视野在线测评]题解(持续更新)

目录:

一、DP

二、图论

  1、最短路

  2、强连通分量

三、利用单调性维护

四、贪心

五、数据结构

  1、并查集

六、数学

  1、计数问题

  2、数学分析 

七、博弈

八、搜索

//////////////////////////////////

 

一、DP:


1003: 

(参见 http://hi.baidu.com/aekdycoin/item/88a8be0bf621c6314ac4a3d5 )

首先对于某个时间段[i,j],我们可以轻松暴力删点以后求1-n的最短路
然后就是一个区间DP的问题
DP[i][j] 表示从第 i 天到第 j天的最优值,于是方程很显然:
DP[i][j] = min{DP[i][w] + K + DP[w+1][j]} (i<=w<j)
  1 /**************************************************************
  2     Problem: 1003
  3     User: pikapika
  4     Language: C++
  5     Result: Accepted
  6     Time:40 ms
  7     Memory:1396 kb
  8 ****************************************************************/
  9  
 10 #include <iostream>
 11 #include <cstdio>
 12 #include <string>
 13 #include <cstring>
 14 #include <queue>
 15 #include <algorithm>
 16 using namespace std;
 17 #define typ long long
 18 #define INF 2000000000LL
 19 const int N = 110;
 20 const int E = 1100;
 21 struct Edge {
 22     int u, v, nex;
 23     typ len;
 24     Edge() {
 25     }
 26     Edge(int _u, int _v, typ _len, int _nex) {
 27         u = _u, v = _v, len = _len, nex = _nex;
 28     }
 29 };
 30 queue<int> Q;
 31 Edge eg[E];
 32 typ dis[N];
 33 int g[N], idx;
 34 bool vis[N];
 35 void addedge(int u, int v, typ len) {
 36     eg[idx] = Edge(u, v, len, g[u]);
 37     g[u] = idx++;
 38 }
 39 typ spfa(int key, int st, int ed, int tot) {
 40     for (int i = 1; i <= tot; ++i)
 41         dis[i] = INF;
 42     memset(vis, 0sizeof(vis));
 43     dis[st] = 0;
 44     while (!Q.empty())
 45         Q.pop();
 46     Q.push(st);
 47     while (!Q.empty()) {
 48         int x = Q.front();
 49         Q.pop();
 50         vis[x] = false;
 51         for (int i = g[x]; ~i; i = eg[i].nex) {
 52             if (key & (1 << eg[i].v))
 53                 continue;
 54             if (eg[i].len + dis[x] < dis[eg[i].v]) {
 55                 dis[eg[i].v] = dis[x] + eg[i].len;
 56                 if (!vis[eg[i].v]) {
 57                     Q.push(eg[i].v);
 58                     vis[eg[i].v] = true;
 59                 }
 60             }
 61         }
 62     }
 63     return dis[ed];
 64 }
 65  
 66 int ar[N], n, m, e;
 67 typ kkcld;
 68 typ d[N][N];
 69  
 70 typ Cal(int l, int r) {
 71     int key = 0;
 72     for (int i = l; i <= r; ++i) {
 73         key |= ar[i];
 74     }
 75     return spfa(key, 1, m, m);
 76 }
 77 typ dp(int l, int r) {
 78     typ &z = d[l][r];
 79     if (~z)
 80         return z;
 81     z = Cal(l, r) * (r - l + 1);
 82     for (int i = l; i + 1 <= r; ++i) {
 83         z = min(dp(l, i) + dp(i + 1, r) + kkcld, z);
 84     }
 85     return z;
 86 }
 87 void input() {
 88     memset(g, -1sizeof(g));
 89     memset(ar, 0sizeof(ar));
 90     idx = 0;
 91     int u, v;
 92     typ len;
 93     for (int i = 0; i < e; ++i) {
 94         scanf("%d%d%lld", &u, &v, &len);
 95         addedge(u, v, len);
 96         addedge(v, u, len);
 97     }
 98     int d, p, a, b;
 99     scanf("%d", &d);
100     for (int i = 0; i < d; ++i) {
101         scanf("%d%d%d", &p, &a, &b);
102         for (int j = a; j <= b; ++j) {
103             ar[j] |= (1 << p);
104         }
105     }
106 }
107 void solve() {
108     memset(d, -1sizeof(d));
109     printf("%lld\n", dp(1, n));
110 }
111 int main() {
112     while (4 == scanf("%d%d%lld%d", &n, &m, &kkcld, &e)) {
113         input();
114         solve();
115     }
116     return 0;
117 }
View Code 


 1009:

KMP预处理+矩阵快速幂加速DP

可以参考( http://hi.baidu.com/wyl8899/item/dc5abdccb571efd597445268 )

f[i, j]代表字符串匹配到第i位时已经匹配了不吉利数字1到j位 时的方案数

KMP预处理状态转移,由于转移方程式都是一样的,可以用矩阵快速幂优化

 1 /**************************************************************
 2     Problem: 1009
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:56 ms
 7     Memory:1276 kb
 8 ****************************************************************/
 9  
10 #include <iostream>
11 #include <cstdio>
12 #include <algorithm>
13 #include <cstring>
14 using namespace std;
15 const int N = 22;
16 struct Matrix {
17     int r, c;
18     int val[N][N];
19     void clear() {
20         memset(val, 0sizeof(val));
21     }
22     void One() {
23         for (int i = 0; i < r; ++i)
24             val[i][i] = 1;
25     }
26 };
27  
28 Matrix M, ans;
29 int n, m, mod;
30 int ar[N], f[N];
31  
32 Matrix multi(Matrix a, Matrix b) {
33     Matrix re;
34     re.r = a.r, re.c = b.c;
35     re.clear();
36     for (int i = 0; i < re.r; ++i) 
37         for (int j = 0; j < re.c; ++j) 
38             for (int k = 0; k < re.c; ++k) 
39                 re.val[i][j] = (re.val[i][j] + (a.val[i][k] * b.val[k][j]) % mod) % mod;
40     return re;
41 }
42 Matrix Pow(Matrix a, int x) {
43     Matrix re;
44     re.clear();
45     re.r = re.c = a.r;
46     re.One();
47     while (x) {
48         if (x & 1) re = multi(re, a);
49         a = multi(a, a);
50         x >>= 1;
51     }
52     return re;
53 }
54 void prepare() {
55     int i, j, p;
56     f[0] = f[1] = 0;
57     M.r = m, M.c = m;
58     M.clear();
59     for (i = 1; i < m; ++i) {
60         j = f[i];
61         while (j && ar[j] != ar[i]) j = f[j];
62         if (ar[j] == ar[i]) f[i + 1] = j + 1;
63         else f[i + 1] = 0;
64     }
65     for (i = 0; i < m; ++i) {
66         for (j = 0; j < 10; ++j) {
67             p = i;
68             while (p && ar[p] != j) p = f[p];
69             if (ar[p] == j && p == m - 1continue;
70             if (ar[p] == j) ++M.val[i][p + 1];
71             else ++M.val[i][p];
72         }
73     }
74     ans.r = 1, ans.c = m;
75     ans.clear();
76     ans.val[0][0] = 1;
77 }
78 int main() {
79     int tot;
80     while (3 == scanf("%d%d%d", &n, &m, &mod)) {
81         for (int i = 0; i < m; ++i)
82             scanf("%1d", &ar[i]);
83         prepare();
84         tot = 0;
85         M = Pow(M, n);
86         ans = multi(ans, M);
87         for (int i = 0; i < m; ++i)
88             tot = (tot + ans.val[0][i]) % mod;
89         printf("%d\n", tot);
90  
91     }
92     return 0;
93 }
View Code 

 

1010:

首先是一个很明显的O(N^2)的dp

dp[i] = dp[j] + min(sum[i] - sum[j] + i - j - 1 + L)^2

dp[i] = min(dp[j] + (sum[i] - sum[j] + i - j - 1 + L)^2) 

可以用线段树或者单调队列优化到O(NlogN) ,参见

jsoi2009论文《用单调性优化动规》( http://wenku.baidu.com/view/83e4fec59ec3d5bbfd0a74e1.html )

《1D\1D动态规划初步》 ( http://wenku.baidu.com/view/681d161ca300a6c30c229f70.html )

 1 /**************************************************************
 2     Problem: 1010
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:204 ms
 7     Memory:2448 kb
 8 ****************************************************************/
 9  
10 #include <iostream>
11 #include <cstdio>
12 #include <algorithm>
13 #include <cstring>
14 #include <queue>
15 using namespace std;
16 #define typ long long
17 const int N = 50010;
18 struct node {
19     int l, r, id;
20     node() {
21     }
22     node(int _l, int _r, int _id) {
23         l = _l, r = _r, id = _id;
24     }
25 };
26  
27 deque<node> Q;
28 typ d[N], sum[N], L, c[N];
29 int n;
30  
31 void input() {
32     for (int i = 1; i <= n; ++i)
33         scanf("%lld", &c[i]);
34     sum[0] = 0;
35     for (int i = 1; i <= n; ++i) 
36         sum[i] = sum[i - 1] + c[i];
37 }
38 typ sqr(typ x) {
39     return x * x;
40 }
41 typ Cal(int l, int r) {
42     return sqr(sum[r] - sum[l - 1] + r - l - L);
43 }
44 void solve() {
45     node u;
46     while (!Q.empty()) 
47         Q.pop_back();
48     Q.push_front(node(1, n, 0));
49     d[0] = 0;
50     for (int i = 1; i <= n; ++i) {
51         u = Q.front();
52         d[i] = d[u.id] + Cal(u.id + 1, i);
53         if (i == n) break;
54         if (Q.front().l < Q.front().r) ++Q.front().l;
55         else Q.pop_front();        
56         while (true) {
57             if (Q.empty()) {
58                 Q.push_back(node(i + 1, n, i));
59                 break;
60             }
61             u = Q.back();
62             if (d[u.id] + Cal(u.id + 1, u.l) >= d[i] + Cal(i + 1, u.l)) {
63                 Q.pop_back();
64                 continue;
65             }
66             if (d[u.id] + Cal(u.id + 1, u.r) <= d[i] + Cal(i + 1, u.r)) {
67                 if (u.r != n) 
68                     Q.push_back(node(u.r + 1, n, i));
69                 break;
70             }
71             int L = u.l, R = u.r;
72             int mid;
73             while (L < R) {
74                 mid = (L + R) >> 1;
75                 if (d[u.id] + Cal(u.id + 1, mid) < d[i] + Cal(i + 1, mid)) {
76                     L = mid + 1;
77                 } else {
78                     R = mid;
79                 }
80             }
81             Q.back().r = L - 1;
82             Q.push_back(node(L, n, i));
83             break;
84         }
85     }
86     printf("%lld\n", d[n]);
87 }
88 int main() {
89     while (2 == scanf("%d%lld", &n, &L)) {
90         input();
91         solve();
92     }
93     return 0;
94 }
View Code 

也可以用经典的斜率优化 复杂度是O(N),参见

 《从一类单调性问题看算法的优化》 ( http://wenku.baidu.com/view/fa5e7243b307e87101f69683.html )

 《数形结合的应用——浅谈动态规划中的斜率优化》

( http://wenku.baidu.com/view/66304ff4ba0d4a7302763ae7.html )

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <queue>
 5 using namespace std;
 6 #define LL long long
 7 const int N = 50010;
 8 
 9 LL d[N], sum[N], L;
10 deque<int> Q;
11 int n;
12 
13 void input() {
14     sum[0] = 0;
15     for (int i = 1; i <= n; ++i) {
16         scanf("%lld", &sum[i]);
17         sum[i] += sum[i - 1];
18     }
19 }
20 LL A(int x) {
21     return (LL)x + sum[x] - 1LL - L;
22 }
23 LL B(int x) {
24     return (LL)x + sum[x];
25 }
26 LL sqr(LL x) {
27     return x * x;
28 }
29 LL Up(int k1, int k2) {
30     return d[k1] - d[k2] + sqr(B(k1)) - sqr(B(k2)) ;
31 }
32 LL Down(int k1, int k2) {
33     return (B(k1) - B(k2)) * 2LL;
34 }
35 bool g(int k1, int k2, int x) {
36     return Up(k1, k2) <= A(x) * Down(k1, k2);
37 }
38 LL Cal(int i, int j) {
39     return d[i] + sqr(j - i - 1 + sum[j] - sum[i] - L);
40 }
41 void solve() {
42     int u, v;
43     while (!Q.empty())
44         Q.pop_back();
45     d[0] = 0;
46     Q.push_back(0);
47     for (int i = 1; i <= n; ++i) {
48         while (Q.size() >= 2) {
49             u = Q.front();
50             Q.pop_front();
51             v = Q.front();
52             if (g(u, v, i)) {
53                 Q.push_front(u); break;
54             }
55         }
56         u = Q.front();
57         d[i] = Cal(u, i);
58         if (i == n) break;
59         while (Q.size() >= 2) {
60             v = Q.back();
61             Q.pop_back();
62             u = Q.back();
63             if (Up(u, v) * Down(v, i) >= Up(v, i) * Down(u, v)) continue;
64             else {
65                 Q.push_back(v); break;
66             }
67         }
68         Q.push_back(i);
69     }
70     printf("%lld\n", d[n]);
71 }
72 int main() {
73     while (2 == scanf("%d%lld", &n, &L)) {
74         input();
75         solve();
76     }
77     return 0;
78 }
View Code 

 

1021:

DP: d[i][j][k] //只允许交换前0到i-1种前,使得第一个人拥有j元,第二个人拥有k元 所花费的最少交换次数

然后枚举第i种货币的分配方案,两层循环分别枚举第一个人,第二个人交换完第i种货币后手里还有几枚。

有的题解说要用最大公约数剪枝,我没有试过,总之速度还算行。

 1 /**************************************************************
 2     Problem: 1021
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:472 ms
 7     Memory:8776 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 #include <cstring>
13  
14 const int INF = (int)(1e9) +10;
15  
16 const int N = 6;
17 int own[3][N];
18  
19 int x1, x2, x3;
20 int sum[3], mon[N];
21  
22 const int MAX = 1000 + 10;
23 int d[2][MAX][MAX];
24  
25 int val[N] = {15102050100};
26  
27 void update(int& x, int y) {
28     if (-1 == x)
29         x = y;
30     else
31         x = std::min(x, y);
32 }
33 void work() {
34     memset(mon, 0sizeof(mon));
35  
36     int tot = 0;
37     for (int i = 0; i < 3; ++i) {
38         sum[i] = 0;
39         for (int j = N - 1; j >= 0; --j) {
40             scanf("%d", &own[i][j]);
41             mon[j] += own[i][j];
42             sum[i] += own[i][j] * val[j];
43         }
44         tot += sum[i];
45     }
46  
47     memset(d[0], -1sizeof(d[0]));
48     d[0][sum[0]][sum[1]] = 0;
49     for (int i = 0; i < N; ++i) {
50         int now = i & 1;
51         memset(d[1 - now], -1sizeof(d[0]));
52  
53         for (int j = 0; j <= tot; ++j) {
54             for (int k = 0; k + j <= tot; ++k) {
55                 if (d[now][j][k] >= 0) {
56                     update(d[1 - now][j][k], d[now][j][k]);
57  
58                     for (int a = 0; a <= mon[i]; ++a) {
59                         for (int b = 0; b + a <= mon[i]; ++b) {
60                             int suma = j + val[i] * (a - own[0][i]);
61                             int sumb = k + val[i] * (b - own[1][i]);
62                             if (suma >= 0 && sumb >= 0 && suma + sumb <= tot) {
63                                 int dis = (std::abs(a - own[0][i]) + std::abs(b - own[1][i]) + std::abs(mon[i] - a - b - own[2][i])) / 2;
64                                 update(d[1 - now][suma][sumb], d[now][j][k] + dis);
65                             }
66                         }
67                     }
68                 }
69             }
70         }
71     }
72     int ea = sum[0], eb = sum[1], ec = sum[2];
73     ea -= x1; eb += x1;
74     eb -= x2; ec += x2;
75     ec -= x3; ea += x3;
76     if (ea < 0 || eb < 0 || ec < 0 || ea + eb + ec != tot || d[N & 1][ea][eb] < 0)
77         puts("impossible");
78     else
79         printf("%d\n", d[N & 1][ea][eb]);
80 }
81 int main() {
82     while (3 == scanf("%d%d%d", &x1, &x2, &x3)) {
83         work();
84     }
85     return 0;
86 }
View Code 

 

1025:

(参见 http://www.google.com.hk/url?sa=t&rct=j&q=[SCOI2009]%E6%B8%B8%E6%88%8F&source=web&cd=9&ved=0CFsQFjAI&url=http%3a%2f%2fabyss.ylen.me%2farchives%2f55&ei=Y3QsUsC-Aa6viQfsmoDYDA&usg=AFQjCNEyp1WqBW9xrWIA8i277vs_PMkKGw )

通过分析,我们发现,如果一个数的循环节是x,那么一定有x个数的循环节是x。因为一个数在他的循环中,不可能两次遇到同一个数。

而排列数就是每个循环节的LCM。

于是我们将问题抽象成:将N分割成k个数(k <= N),即(a1+....+ak) = N,问我们LCM(a1, a2, ...., ak)有多少种?

由于1对LCM是没有影响的,所以问题又变成了(a1+a2+....+ak) <= N,那么LCM(a1, a2, ..., ak)有多少种?

我们可以DP解决

d[i][j]//前0到i-1个素数组成和为j时的方案数(j = (pime[0]^C0 + prime[1]*C1 + .... + prime[i - 1]*Ci - 1)

= d[i - 1][j] + sigme{d[i - 1][j - k]};//k = prime[i]^c;(c >= 1)

 1 /**************************************************************
 2     Problem: 1025
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:96 ms
 7     Memory:8776 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <cstring>
12 #include <algorithm>
13 #include <vector>
14 typedef long long ll;
15  
16 const int N = 1000 + 10;
17 ll d[N][N];
18 bool vis[N];
19  
20 int n;
21 std::vector<int> prime;
22  
23 void prepare() {
24     prime.clear();
25     memset(vis, 0sizeof(vis));
26     for (int i = 2; i <= n; ++i) {
27         if (!vis[i]) {
28             prime.push_back(i);
29             for (int j = i * i; j <= n; j += i)
30                 vis[j] = true;
31         }
32     }
33 }
34 int main() {
35     while (1 == scanf("%d", &n)) {
36         prepare();
37  
38         memset(d, 0sizeof(d));
39         d[0][0] = 1;
40         for (int i = 0; i < prime.size(); ++i) {
41             for (int j = 0; j <= n; ++j) {
42                 if (d[i][j] == 0continue;
43                 d[i + 1][j] += d[i][j];
44                 for (int z = prime[i]; z + j <= n; z *= prime[i]) {
45                     d[i + 1][j + z] += d[i][j];
46                 }
47             }
48         }
49  
50         ll ans = 0;
51         for (int i = 0; i <= n; ++i)
52             ans += d[prime.size()][i];
53         printf("%lld\n", ans);
54     }
55     return 0;
56 }
View Code 

 

1026:

[SCOI2009]windy数

 数位dp:

d[i][j]//以i做为开头,长度为j的数,可以组成多少个windy数 (注意,当i为0的时候,i不能是前导0)

 1 /**************************************************************
 2     Problem: 1026
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:0 ms
 7     Memory:804 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 #include <cstring>
13 #include <vector>
14  
15 const int N = 10 + 2;
16 int d[N][N];
17  
18 int dp(int x, int len) {
19     if (~d[x][len]) return d[x][len];
20     if (1 == len) {
21         return d[x][len] = 1;
22     }
23     d[x][len] = 0;
24     for (int i = x + 2; i < 10; ++i)
25         d[x][len] += dp(i, len - 1);
26     for (int i = x - 2; i >= 0; --i) {
27         d[x][len] += dp(i, len - 1);
28     }
29     return d[x][len];
30 }
31 int a, b;
32 std::vector<int>ar;
33  
34 void dfs(int& re, int len) {
35     if (len == 1) {
36             int z = 0;
37         for (int i = ar[len] + 2; i <= ar[len - 1]; ++i)
38             ++re, ++z;
39         for (int i = std::min(ar[len] - 2, ar[len - 1]); i >= 0; --i)
40             ++re, ++z;
41     } else {
42         for (int i = std::min(ar[len] - 2, ar[len - 1]); i >= 0; --i) {
43             if (i == ar[len - 1]) {
44                 dfs(re, len - 1);
45             } else {
46                 re += dp(i, len);
47             }
48         }
49         for (int i = ar[len] + 2; i <= ar[len - 1]; ++i) {
50             if (i == ar[len - 1]) {
51                 dfs(re, len - 1);
52             } else {
53                 re += dp(i, len);
54             }
55         }
56     }
57 }
58 int Cal(int x) {
59     if (x < 0return 0;
60     else if (0 == x) return 1;
61  
62     ar.clear();
63     int re = 0;
64     while (x) {
65         ar.push_back(x % 10);
66         x /= 10;
67     }
68  
69     if (1 == ar.size())
70         return ar[0] + 1;
71  
72     int len = ar.size();
73     for (int i = 1; i < 10; ++i)
74         for (int j = 1;  j < len; ++j)
75             re += dp(i, j);
76     ++re;
77     for (int i = 1; i <= ar[len - 1]; ++i) {
78         if (i == ar[len - 1]) {
79             dfs(re, len - 1);
80         } else {
81             re += dp(i, len);
82         }
83     }
84     return re;
85 }
86 void work() {
87     int ansa = Cal(a - 1);
88     int ansb = Cal(b);
89     printf("%d\n", ansb - ansa);
90 }
91 int main() {
92     memset(d, -1sizeof(d));
93  
94     while (2 == scanf("%d%d", &a, &b)) {
95         work();
96     }
97     return 0;
98 }
View Code 

 

 1037:

DP:d[i][j][k]/以i-1个数结尾的所有连续断都满足条件,以第i个数结尾的所有连续段中,男生最多比女生多j个,女生最多比男生多k个

d[i][j][k] = d[i - 1][j + 1][k - 1] //第i个是男生

+ d[i - 1][j - 1][k + 1];//第i个是女生

 由于内存问题,需要用滚动数组

 1 /**************************************************************
 2     Problem: 1037
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:428 ms
 7     Memory:1588 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 #include <cstring>
13  
14 const int mod = 12345678;
15  
16 int n, m, lim;
17  
18 const int N = 20 + 5;
19 const int M = 150 + 10;
20 int d[2][M][N][N];
21  
22 void work() {
23     memset(d, 0sizeof(d));
24     d[0][0][0][0] = 1;
25  
26     for (int i = 1; i <= (n + m); ++i) {
27         int now = i & 1, pre = 1 - now;
28         memset(d[now], 0sizeof(d[0]));
29  
30         for (int j = 0; j <= std::min(i, n); ++j)
31         for (int k1 = 0; k1 <= std::min(i, lim); ++k1) {
32             for (int k2= 0; k2 <= std::min(lim, i - j); ++k2) {
33                 int& z = d[now][j + 1][k1 + 1][std::max(0, k2 - 1)];
34                 z = (z + d[pre][j][k1][k2]) % mod;
35                 int& zz = d[now][j][std::max(0, k1 - 1)][k2 + 1];
36                 zz = (zz + d[pre][j][k1][k2]) % mod;
37             }
38         }
39     }
40  
41     int sum = 0;
42     for (int k1 = 0; k1 <= lim; ++k1)
43         for (int k2 = 0; k2 <= lim; ++k2)
44             sum = (sum + d[(n + m) & 1][n][k1][k2]) % mod;
45     printf("%d\n", sum);
46 }
47  
48 int main() {
49     while (3 == scanf("%d%d%d", &n, &m, &lim)) {
50         work();
51     }
52     return 0;
53 }
View Code 

 

1044:

[HAOI2008]木棍分割

第一问是经典的二分题,二分最大长度的最小值,贪心判断

第二问dp做:

d[i,j] //前j个木棍,恰好砍了i刀的方案数

= d[i - 1, k]; //k是所有满足 sum(k + 1, j) <= 第一问的结果的值

我们发现,此处具有决策的单调性,所以我们模拟队列解决

另外由于空间问题需要滚动数组

 1 /**************************************************************
 2     Problem: 1044
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:7056 ms
 7     Memory:1588 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 #include <cstring>
13 const int mod = 10007;
14 const int N = 50000 + 10;
15  
16 int ar[N], sum[N], d[2][N];
17  
18 int n, m;
19  
20 bool judge(int val) {
21     int re = 0, tot = 0;
22     for (int i = 1; i <= n; ++i) {
23         tot = tot + ar[i];
24         if (tot > val) {
25             ++re;
26             tot = ar[i];
27         }
28     }
29     if (re <= m) return true;
30     else return false;
31 }
32 void work() {
33     sum[0] = ar[0] = 0;
34     for (int i = 1; i <= n; ++i) {
35         scanf("%d", &ar[i]);
36         sum[i] = sum[i - 1] + ar[i];
37     }
38     int l, r, mid;
39     l = 0; r = sum[n];
40     while (r - l > 1) {
41         mid = (l + r) / 2;
42         if (judge(mid))
43             r = mid;
44         else
45             l = mid;
46     }
47     printf("%d ", r);
48  
49     int ans = r;
50     memset(d[0], 0sizeof(d[0]));
51     for (int i = 1; i <= n; ++i)
52         if (sum[i] <= ans)
53             ++d[0][i];
54         else
55             break;
56  
57     int idx;
58     int tot, presum = 0, a = d[0][n];
59  
60     for (int j = 1; j <= m; ++j) {
61         int now = j & 1, pre = 1 - now;
62         memset(d[now], 0sizeof(d[now]));
63  
64         presum = tot = 0;
65         idx = 1;
66  
67         for (int i = 2; i <= n; ++i) {
68             tot = tot + ar[i];
69             presum = (presum + d[pre][i - 1]) % mod;
70  
71             while (tot > ans) {
72                 tot = tot - ar[idx + 1];
73                 presum = (presum - d[pre][idx] + mod) % mod;
74                 ++idx;
75             }
76  
77             d[now][i] = presum;
78         }
79         a = (a + d[now][n]) % mod;
80     }
81  
82     printf("%d\n", a);
83 }
84 int main() {
85     while (2 == scanf("%d%d", &n, &m)) {
86         work();
87     }
88     return 0;
89 }
View Code

 

1046:

首先倒着做O(NlogN)的LIS,然后深搜

 1 /**************************************************************
 2     Problem: 1046
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:1792 ms
 7     Memory:928 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 #include <cstring>
13 #include <vector>
14  
15 const int INF = (int)1e9 + 10;
16 const int N = 10000 + 10;
17 int ar[N], g[N], d[N], n;
18  
19 int bsearch(int val) {
20     int l, r, mid;
21     l = 0,r = n;
22     while (r - l >1) {
23         mid = (l + r) >> 1;
24         if (g[mid] <= val)
25             r = mid;
26         else
27             l = mid;
28     }
29     g[r] = val;
30     return r;
31 }
32 std::vector<int> ans;
33  
34 void work() {
35     for (int i = 1; i <= n; ++i)
36         scanf("%d", &ar[i]);
37     std::fill(g, g + n + 2, -INF);
38     int mx = 0;
39     for (int i = 1; i <= n; ++i) {
40         d[n - i + 1] = bsearch(ar[n - i + 1]);
41         mx = std::max(mx, d[n + 1 - i]);
42     }
43  
44     int Q, v;
45     scanf("%d", &Q);
46     while (Q --) {
47         scanf("%d", &v);
48         if (v > mx)
49             puts("Impossible");
50         else {
51             ans.clear();
52             ans.push_back(-INF);
53             for (int i = 1; i <= n; ++i) {
54                 if (d[i] >= v && ar[i] > ans[ans.size() - 1]) {
55                     ans.push_back(ar[i]);
56                     if (--v == 0break;
57                 }
58             }
59             for (int i = 1; i < ans.size(); ++i) {
60                 if (i > 1)
61                     putchar(' ');
62                 printf("%d", ans[i]);
63             }
64             putchar('\n');
65         }
66     }
67 }
68 int main() {
69     while (1 == scanf("%d", &n)) {
70         work();
71     }
72     return 0;
73 }
View Code 

 

1048:

记忆化搜索 

陈题了,黑书上DP那章有介绍, 要求均方差最小,可以先将公式变形,发现只要各部分平方和最大即可

 1 /**************************************************************
 2     Problem: 1048
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:104 ms
 7     Memory:1788 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 #include <cstring>
13 #include <cmath>
14 const int INF = (int)(1e9) + 10;
15  
16 const int N = 10 + 2;
17 int d[N][N][N][N][N];//x1,y1,x2,y2,lim
18  
19 int r, c, lim;
20  
21 int ar[N][N];
22 int sum(int x1, int y1, int x2, int y2) {
23     int re = 0;
24     for (int i = x1; i <= x2; ++i)
25         for (int j = y1; j <= y2; ++j)
26             re += ar[i][j];
27     return re * re;
28 }
29 int dp(int x1, int y1, int x2, int y2, int ti) {
30     int& z = d[x1][y1][x2][y2][ti];
31     if (~z) return z;
32     if (1 == ti) {
33         return z = sum(x1, y1, x2, y2);
34     }
35     z = INF;
36     for (int i = x1; i < x2; ++i)
37         for (int j = 1; j < ti; ++j)
38             z = std::min(z, dp(x1, y1, i, y2, j) + dp(i + 1, y1, x2, y2, ti - j));
39     for (int i = y1; i < y2; ++i) {
40         for (int j = 1; j < ti; ++j)
41             z = std::min(z, dp(x1, y1, x2, i, j) + dp(x1, i + 1, x2, y2, ti - j));
42     }
43     return z;
44 }
45 void work() {
46     int tot = 0;
47     for (int i = 1; i <= r; ++i)
48         for (int j = 1; j <= c; ++j) {
49             scanf("%d", &ar[i][j]);
50             tot += ar[i][j];
51         }
52     memset(d, -1sizeof(d));
53  
54     int mx = dp(11, r, c, lim);
55     //printf("%d\n", mx);
56  
57     double avg = (double)tot / lim;
58     double ans = std::sqrt((double)mx / lim - 2. * avg * tot / lim + avg * avg);
59     printf("%.2f\n", ans);
60 }
61 int main() {
62     while (3 == scanf("%d%d%d", &r, &c, &lim)) {
63         work();
64     }
65     return 0;
66 }
View Code 

 

1049:

我们要修改最少的数让原序列变成一个严格单调上升序列(不重复)。

我们可以先读入每一个数,然后ar[i] - i后,问题转化成为修改最少的数让原序列变成非严格单调上升序列(可重复),

由此我们可以知道,最终序列中的每一个数在原序列都出现过。我们可以将原序列的数提取出来,离散化,然后做dp。

f[i,j]//前i个数已经非严格单调增,且第i位的值<=j,

f[i,j] = min(f[i, j - 1], f[i - 1, j - 1] + cost);  //枚举第i位的值

 1 /**************************************************************
 2     Problem: 1049
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:5108 ms
 7     Memory:1352 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 struct node {
13     int val, cos;
14 };
15  
16 const int N = 35000 + 3;
17 node d[N];
18  
19 int n;
20 int ar[N], x[N];
21  
22 inline void update(node& a, int& nval, int& ncos) {
23     if (a.val >= 0) {
24         if (nval < a.val) {
25             a.val = nval;
26             a.cos = ncos;
27         } else if (nval == a.val) {
28             a.cos = std::min(a.cos, ncos);
29         }
30     } else {
31         a.val = nval;
32         a.cos = ncos;
33     }
34 }
35 void work() {
36     for (int i = 1; i <= n; ++i) {
37         scanf("%d", &ar[i]);
38         ar[i] = ar[i] - i;
39         x[i - 1] = ar[i];
40     }
41  
42     std::sort(x, x + n);
43     int idx = std::unique(x, x + n) - x;
44  
45     for (int i = 0; i < idx; ++i)
46         d[i].cos = d[i].val = 0;
47  
48     for (int i = 1; i <= n; ++i) {
49             for (int j = 0; j < idx; ++j) {
50                 if (d[j].val >= 0) {
51                     if (ar[i] != x[j])
52                         d[j].val = d[j].val + 1;
53                     d[j].cos = d[j].cos + std::abs(ar[i] - x[j]);
54                 }
55                 if (j > 0 && d[j - 1].val >= 0) {
56                     update(d[j], d[j - 1].val, d[j - 1].cos);
57                 }
58 //                printf("%d %d %d %d\n", i, j, d[now][j].val, d[now][j].cos);
59             }
60     }
61  
62     node ans;
63     ans.val = -1; ans.cos = 0;
64     for (int i = 0; i < idx; ++i)
65         if (d[i].val >= 0)
66             update(ans, d[i].val, d[i].cos);
67     printf("%d\n%d\n", ans.val, ans.cos);
68 }
69 int main() {
70     while (1 == scanf("%d", &n)) {
71         work();
72     }
73     return 0;
74 }
View Code 


1072:

[SCOI2007]排列perm

c++用STL中next_permutation枚举,set判重,可以直接过,但是速度不够快。

 1 /**************************************************************
 2     Problem: 1072
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:4320 ms
 7     Memory:7804 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 #include <set>
13 #include <cstring>
14 typedef long long ll;
15 const int N = 10 + 3;
16  
17 std::set<ll> v;
18  
19 char s[N];
20 int ar[N];
21  
22 void work() {
23     int d;
24     v.clear();
25  
26     scanf("%s%d", s, &d);
27  
28     int idx = 0;
29     for (int i = 0; s[i]; ++i) {
30         ar[idx ++] = s[i] - '0';
31     }
32  
33     ll ans = 0;
34  
35     std::sort(ar, ar + idx);
36     do {
37         ll num = 0;
38         for (int i = 0; i < idx; ++i) {
39             num = num * 10 + ar[i];
40         }
41         if (v.count(num)) continue;
42         else if (num % d == 0) {
43             ++ans;
44             v.insert(num);
45         }
46     } while (std::next_permutation(ar, ar + idx));
47  
48     printf("%lld\n", ans);
49 }
50 int main() {
51     int T;
52     scanf("%d", &T);
53     while (T -- > 0) {
54         work();
55     }
56     return 0;
57 }
View Code 

比较好的方法是状压DP,

d[s][i] //集合s表示已经被取走了的数的集合,i表示这些数可以有多少种组成方式使得组成的数mod d = i 

我们有转移方程

d[s | (1 << ar[k])][(i * 10 + ar[k])  % d]  += d[s][i]; //s中没有第k个数 

 1 /**************************************************************
 2     Problem: 1072
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:824 ms
 7     Memory:8828 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12 #include <cstring>
13 typedef long long ll;
14 const int N = 1000 + 2;
15  
16 char ch[N];
17 int ar[N];
18  
19 ll dp[1 << 10][N];
20  
21 void work() {
22     int d;
23     scanf("%s%d", ch, &d);
24  
25     int idx = 0;
26     for (int i = 0; ch[i]; ++i) {
27         ar[idx ++] = ch[i] - '0';
28     }
29  
30     ll ans = 0, tmp = 1;
31     std::sort(ar, ar + idx);
32  
33     for (int i = 0; i < idx;) {
34         int cnt = 0;
35         for (int j = i; j < idx; ++j) {
36             if (ar[j] == ar[i]) ++cnt;
37             else {
38                 i = j; break;
39             }
40             if (j + 1 == idx) i = idx;
41         }
42         for (int j = 2; j <= cnt; ++j) {
43             tmp = tmp * j;
44         }
45     }
46  
47     memset(dp, 0sizeof(dp));
48     dp[0][0] = 1;
49  
50     int mx = 1 << idx;
51     for (int i = 0; i < idx; ++i) {
52         if (i == 0) {
53             for (int k = 0; k < idx; ++k)
54                 dp[1 << k][ar[k] % d] = 1;
55  
56         } else {
57             int s = (1 << i) - 1;
58             while (s < mx) {
59                 for (int j = 0; j < d; ++j) {
60                     if (dp[s][j] > 0)
61                         for (int k = 0; k < idx; ++k) {
62                             if (!(s >> k & 1)) {
63                                 dp[s | (1 << k)][(j * 10 + ar[k]) % d] += dp[s][j];
64                             }
65                         }
66                 }
67                 int x = s & -s;
68                 int y = s + x;
69                 s = ((s & ~y) / x >> 1) | y;
70             }
71         }
72     }
73     printf("%lld\n", dp[mx - 1][0] / tmp);
74 }
75 int main() {
76     int T;
77     scanf("%d", &T);
78     while (T -- > 0) {
79         work();
80     }
81     return 0;
82 }
View Code 

 

1084:

 我们注意到列最大只有2,所以分开讨论,当列只有1的时候

d[i, j]//已经取完前i行,累计取了j个矩阵

-> d[k,j] //第i + 1行到第k行不取

-> d[k,j + 1]//取第i+1到第k行的矩阵

列为2的时候差不多,d[i,j,k]//第一列取到了第i行,第2列取到了第j行,累计取了k个矩阵

  1 /**************************************************************
  2     Problem: 1084
  3     User: pikapika
  4     Language: C++
  5     Result: Accepted
  6     Time:1548 ms
  7     Memory:1352 kb
  8 ****************************************************************/
  9  
 10 #include <cstdio>
 11 #include <algorithm>
 12 #include <cstring>
 13  
 14 const int INF = (int)(1e9) + 10;
 15  
 16 const int R = 100 + 3;
 17 const int C = 2 + 3;
 18 const int G = 10 + 3;
 19 int d[R][R][G];
 20 int rd[R][G];
 21  
 22 int ar[R][C];
 23  
 24 int r, c, lim;
 25  
 26 int sum(int x1, int y1, int x2, int y2) {
 27     int re = 0;
 28     for (int i = x1; i <= x2; ++i) {
 29         for (int j = y1; j <= y2; ++j) {
 30             re += ar[i][j];
 31         }
 32     }
 33     return re;
 34 }
 35 void update(int& z, int v) {
 36     if (-INF == z)
 37         z = v;
 38     else
 39         z = std::max(z, v);
 40 }
 41 void work() {
 42     for (int i = 1; i <= r; ++i)
 43         for (int j = 1; j <= c; ++j)
 44             scanf("%d", &ar[i][j]);
 45  
 46     int ans = - INF;
 47     if (1 == c) {
 48         for (int i = 0; i <= r; ++i)
 49             for (int j = 0; j <= lim; ++j)
 50                 rd[i][j] = -INF;
 51         rd[0][0] = 0;
 52         for (int i = 0; i < r; ++i) {
 53             for (int j = 0; j <= lim; ++j) {
 54                 if (rd[i][j] == -INF) continue;
 55                 for (int k = i + 1; k <= r; ++k) {
 56                     update(rd[k][j], rd[i][j]);
 57                     update(rd[k][j + 1], rd[i][j] + sum(i + 11, k, 1));
 58                     if (j + 1 == lim) update(ans, rd[k][j + 1]);
 59                 }
 60             }
 61         }
 62     } else {
 63         for (int i = 0; i <= r; ++i)
 64             for (int j = 0; j <= r; ++j)
 65                 for (int k = 0; k <= lim; ++k)
 66                     d[i][j][k] = -INF;
 67  
 68         d[0][0][0] = 0;
 69         for (int i = 0; i <= r; ++i)
 70         for (int j = 0; j <= r; ++j) {
 71             for (int k = 0; k <= lim; ++k) {
 72                 if (d[i][j][k] == -INF) continue;
 73                 int z = std::max(i + 1, j + 1);
 74                 for (int to = z; to <= r; ++to) {
 75                     update(d[to][to][k], d[i][j][k]);
 76                     update(d[to][to][k + 1], d[i][j][k] + sum(z, 1, to, 2));
 77                     if (k + 1 == lim) update(ans, d[to][to][k + 1]);
 78                 }
 79                 for (int to = i + 1; to <= r; ++to) {
 80                     update(d[to][j][k], d[i][j][k]);
 81                     update(d[to][j][k + 1], d[i][j][k] + sum(i + 11, to, 1));
 82                     if (k + 1 == lim) update(ans, d[to][j][k + 1]);
 83                 }
 84                 for (int to = j + 1; to <= r; ++to) {
 85                     update(d[i][to][k], d[i][j][k]);
 86                     update(d[i][to][k + 1], d[i][j][k] + sum(j + 12, to, 2));
 87                     if (k + 1 == lim) update(ans, d[i][to][k + 1]);
 88                 }
 89             }
 90         }
 91     }
 92  
 93     if (0 == lim) ans = 0;
 94  
 95     printf("%d\n", ans);
 96 }
 97 int main() {
 98     while (3 == scanf("%d%d%d", &r, &c, &lim)) {
 99         work();
100     }
101     return 0;
102 }
View Code 


///////////////////////////////////// 

二、图论

1、最短路


1001:

[BeiJing2006]狼抓兔子

点太多,网络流会T掉。利用平面图性质转换成偶图,然后将网络流转换成最短路,参见周冬论文 《两极相通——浅谈最大最小定理在信息学竞赛中的应用》

(链接君 http://wenku.baidu.com/view/f8239d1910a6f524ccbf85ce.html )

  1 /**************************************************************
  2     Problem: 1001
  3     User: pikapika
  4     Language: C++
  5     Result: Accepted
  6     Time:3772 ms
  7     Memory:120416 kb
  8 ****************************************************************/
  9  
 10 #include <iostream>
 11 #include <cstdio>
 12 #include <algorithm>
 13 #include <cstring>
 14 #include <queue>
 15 using namespace std;
 16 #define typ int
 17 #define INF 1000000000
 18 const int N = 2000005;
 19 const int E = N * 3;
 20 struct Edge {
 21     int u, v, nex;
 22     typ len;
 23 };
 24  
 25 int Q[N], head, tail;
 26 Edge eg[E];
 27 typ dis[N];
 28 int g[N], idx, n, m, st, ed;
 29 bool vis[N];
 30  
 31 void addedge(int u, int v, typ len) {
 32     eg[idx].u = u, eg[idx].v = v, eg[idx].len = len, eg[idx].nex = g[u];
 33     g[u] = idx++;
 34 }
 35 typ spfa(int st, int ed, int tot) {
 36     for (int i = 1; i <= tot; ++i)
 37         dis[i] = INF;
 38     memset(vis, 0sizeof(vis));
 39     head = tail = 0;   
 40     dis[st] = 0;
 41     Q[tail++] = st;
 42     while (head != tail) {
 43         int x = Q[head++];
 44         if (head == N) head = 0;
 45         vis[x] = false;
 46         for (int i = g[x]; ~i; i = eg[i].nex) {
 47             if (eg[i].len + dis[x] < dis[eg[i].v]) {
 48                 dis[eg[i].v] = dis[x] + eg[i].len;
 49                 if (!vis[eg[i].v]) {
 50                     Q[tail++] = eg[i].v;
 51                     if (tail == N) tail = 0;
 52                     vis[eg[i].v] = true;
 53                 }
 54             }
 55         }
 56     }
 57     return dis[ed];
 58 }
 59 void input() {
 60     int id, nid, len;
 61     memset(g, -1sizeof(g));
 62     idx = 0;
 63     st = 0; ed = (n - 2) * 2 * m + (m - 1) * 2 + 1;
 64     for (int i = 1; i <= n; ++i) {
 65         for (int j = 1; j <= m - 1; ++j) {
 66             id = m * 2 * (i - 1) + j * 2;
 67             nid = m * 2 * (i - 2) + j * 2 - 1;
 68             scanf("%d", &len);
 69             if (1 == i) {
 70                 addedge(ed, id, len);
 71                 addedge(id, ed, len);
 72             } else if (i == n) {
 73                 addedge(nid, st, len);
 74                 addedge(st, nid, len);
 75             } else {
 76                 addedge(id, nid, len);
 77                 addedge(nid, id, len);
 78             }
 79         }
 80     }
 81     for (int i = 1; i <= n - 1; ++i)
 82         for (int j = 1; j <= m; ++j) {
 83             scanf("%d", &len);
 84             id = m * 2 * (i - 1) + j * 2 - 1;
 85             nid = m * 2 * (i - 1) + (j - 1) * 2;
 86             if (1 == j) {
 87                 addedge(st, id, len);
 88                 addedge(id, st, len);
 89             } else if (m == j) {
 90                 addedge(nid, ed, len);
 91                 addedge(ed, nid, len);
 92             } else {
 93                 addedge(id, nid, len);
 94                 addedge(nid, id, len);
 95             }
 96         }
 97     for (int i = 1; i <= n - 1; ++i)
 98         for (int j = 1; j <= m - 1; ++j) {
 99             scanf("%d", &len);
100             id = m * 2 * (i - 1) + j * 2;
101             nid = id - 1;
102             addedge(id, nid, len);
103             addedge(nid, id, len);
104         }
105 }
106 void solve() {
107     int ans = spfa(st, ed, ed);
108     printf("%d\n", ans);
109 }
110 int main() {
111     int u, mx;
112     while (2 == scanf("%d%d", &n, &m)) {
113         if (1 == n && 1 == m) puts("0");
114         else if (1 == n) {
115             mx = INF;
116             for (int i = 1; i <= m - 1; ++i) {
117                 scanf("%d", &u);
118                 mx = min(u, mx);
119             }
120             printf("%d", mx); continue;
121         } else if (1 == m) {
122             mx = INF;
123             for (int i = 1; i <= n - 1; ++i) {
124                 scanf("%d", &u);
125                 mx = min(mx, u);
126             }
127             printf("%d", mx); continue;
128         }
129         input();
130         solve();
131     }
132     return 0;
133 }
羞涩的代码君

 

 1050:

枚举最小的边,对最短路变形暴力跑一遍。if (max(d[i], w[i, j]) < d[j]) d[j] = max(d[i], w[i, j]);

  1 /**************************************************************
  2     Problem: 1050
  3     User: pikapika
  4     Language: C++
  5     Result: Accepted
  6     Time:4128 ms
  7     Memory:996 kb
  8 ****************************************************************/
  9  
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <queue>
 13 #include <vector>
 14 #include <algorithm>
 15  
 16 typedef std::pair<intint>pii;
 17 struct Edge {
 18     int v, nex, len;
 19     Edge(){}
 20     Edge(int _v, int _len, int _nex) {
 21         nex = _nex, len = _len, v = _v;
 22     }
 23 };
 24  
 25 const int INF = 30000 + 10;
 26 const int N = 500 + 10;
 27 const int E = 10000 + 10;
 28  
 29 std::vector<int>prime;
 30 bool vv[30000 + 10];
 31  
 32 void prepare() {
 33     memset(vv, 0sizeof(vv));
 34     for (int i = 2; i <= 30000; ++i) {
 35         if (!vv[i]) {
 36             prime.push_back(i);
 37             for (int j = i * i; j <= 30000; j += i)
 38                 vv[j] = true;
 39         }
 40     }
 41 }
 42  
 43 int g[N], idx;
 44 Edge eg[E];
 45 void addedge(int u, int v, int len) {
 46     eg[idx] = Edge(v, len, g[u]);
 47     g[u] = idx++;
 48 }
 49  
 50 int dis[N];
 51 bool vis[N];
 52 int n, m;
 53 pii Cal(int val, int s, int t) {
 54     std::fill(dis, dis + n + 2, INF);
 55     memset(vis, 0sizeof(vis));
 56  
 57     dis[s] = 0;
 58     std::queue<int> Q;
 59     Q.push(s);
 60  
 61     while (!Q.empty()) {
 62         int u = Q.front(); Q.pop();
 63         vis[u] = false;
 64         for (int i = g[u]; ~i; i = eg[i].nex) {
 65             if (eg[i].len < val) continue;
 66             int l = std::max(dis[u], eg[i].len);
 67             int to = eg[i].v;
 68             if (l < dis[to]) {
 69                 dis[to] = l;
 70                 if (!vis[to]) {
 71                     vis[to] = true;
 72                     Q.push(to);
 73                 }
 74             }
 75         }
 76     }
 77     if (dis[t] == INF) return pii(INF, 1);
 78     else {
 79         pii re(dis[t], val);
 80         for (int i = 0; i < prime.size(); ++i) {
 81             while (0 == re.first % prime[i] && 0 == re.second % prime[i]) {
 82                 re.first /= prime[i];
 83                 re.second /= prime[i];
 84             }
 85         }
 86         return re;
 87     }
 88 }
 89  
 90 pii Min(pii a, pii b) {
 91     if (a.first * b.second < b.first * a.second)
 92         return a;
 93     else return b;
 94 }
 95 int x[N];
 96 void work() {
 97     memset(g, -1sizeof(g));
 98     idx = 0;
 99  
100     int idy = 0;
101     int u, v, len;
102     for (int i = 0; i < m; ++i) {
103         scanf("%d%d%d", &u, &v, &len);
104         addedge(u, v, len);
105         addedge(v, u, len);
106         x[idy ++] = len;
107     }
108  
109     std::sort(x, x + idy);
110     idy = std::unique(x, x + idy) - x;
111  
112     int s, t;
113     scanf("%d%d", &s, &t);
114  
115     pii ans(INF, 1);
116     for (int i = 0; i < idy; ++i)
117             ans = Min(ans, Cal(x[i], s, t));
118  
119     if (ans.first == INF) {
120         puts("IMPOSSIBLE");
121     } else {
122         if (ans.second == 1)
123             printf("%d\n", ans.first);
124         else
125             printf("%d/%d\n", ans.first, ans.second);
126     }
127 }
128 int main() {
129     prepare();
130     while (2 == scanf("%d%d", &n, &m)) {
131         work();
132     }
133     return 0;
134 }
View Code 

 

2、强连通分量


1051:

先求强连通块,然后重构图,统计出度为0的块的个数,如果为1,输出那一块的大小,如果大于一输出零

  1 /**************************************************************
  2     Problem: 1051
  3     User: pikapika
  4     Language: C++
  5     Result: Accepted
  6     Time:80 ms
  7     Memory:1928 kb
  8 ****************************************************************/
  9  
 10 #include <cstdio>
 11 #include <iostream>
 12 #include <cstring>
 13 #include <algorithm>
 14 #include <vector>
 15 #include <stack>
 16 using namespace std;
 17 #define INF 1000000000;
 18 const int N = 10010;
 19 const int E = 50010;
 20 struct find_gcc {
 21     struct Edge {
 22         int v, nex;
 23     };
 24     Edge eg[E];
 25     int g[N], idx;
 26     int pre[N], lowlink[N], dfs_clock, scc_cnt;
 27     int sccno[N], S[N], top;
 28     void dfs(int u) {
 29         int v;
 30         pre[u] = lowlink[u] = ++dfs_clock;
 31         S[top++] = u;
 32         for (int i = g[u]; ~i; i = eg[i].nex) {
 33             v = eg[i].v;
 34             if (!pre[v]) {
 35                 dfs(v);
 36                 lowlink[u] = min(lowlink[u], lowlink[v]);
 37             } else if (!sccno[v]) {
 38                 lowlink[u] = min(lowlink[u], pre[v]);
 39             }
 40         }
 41         if (lowlink[u] == pre[u]) {
 42             ++scc_cnt;
 43             while (top != 0) {
 44                 int x = S[top - 1];
 45                 --top;
 46                 sccno[x] = scc_cnt;
 47                 if (x == u)
 48                     break;
 49             }
 50         }
 51     }
 52     void find(int n) {
 53         memset(sccno, 0sizeof(sccno));
 54         memset(pre, 0sizeof(pre));
 55         scc_cnt = dfs_clock = top = 0;
 56         for (int i = 0; i < n; ++i) {
 57             if (!pre[i])
 58                 dfs(i);
 59         }
 60     }
 61     void addedge(int u, int v) {
 62         eg[idx].v = v, eg[idx].nex = g[u];
 63         g[u] = idx++;
 64     }
 65     void clear(int n) {
 66         memset(g, -1sizeof(g));
 67         idx = 0;
 68     }
 69 } sol;
 70 int n, m;
 71 bool key[N];
 72 void input() {
 73     sol.clear(n);
 74     int u, v;
 75     for (int i = 0; i < m; ++i) {
 76         scanf("%d%d", &u, &v);
 77         --u, --v;
 78         sol.addedge(u, v);
 79     }
 80 }
 81 void solve() {
 82     sol.find(n);
 83     if (1 == sol.scc_cnt) {
 84         printf("%d\n", n);
 85     } else {
 86         memset(key, 0sizeof(key));
 87         for (int i = 0; i < n; ++i)
 88             for (int j = sol.g[i]; ~j; j = sol.eg[j].nex) {
 89                 int v = sol.eg[j].v;
 90                 if (sol.sccno[v] != sol.sccno[i]) 
 91                     key[sol.sccno[i]] = true;
 92             }
 93         int cnt = 0, idx;
 94         for (int i = 1; i <= sol.scc_cnt; ++i) {
 95             if (!key[i]) {
 96                 idx = i;
 97                 ++cnt;
 98             }
 99         }
100         if (cnt == 1) {
101             cnt = 0;
102             for (int i = 0; i < n; ++i)
103                 if (sol.sccno[i] == idx) ++cnt;
104             printf("%d\n", cnt);
105         } else puts("0");
106     }
107 }
108 int main() {
109     while (2 == scanf("%d%d", &n, &m)) {
110         input();
111         solve();
112     }
113     return 0;
114 }
View Code 


///////////////////////////////////////

三、利用单调性维护


1012:

可以用单调栈来做,维护一个单调增的栈(如果Ai > Aj 并且 i > j, 那么Ai 总是比 Aj 更加有可能成为答案),然后每次二分查找答案。

当然,更加暴力的方法是直接线段树搞。

 1 /**************************************************************
 2     Problem: 1012
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:452 ms
 7     Memory:2836 kb
 8 ****************************************************************/
 9  
10 #include <iostream>
11 #include <cstdio>
12 #include <algorithm>
13 #include <cstring>
14  
15 const int N = 200010;
16 int sta[N], ar[N], c, cc, m, d;
17 char s[10];
18 int bSearch(int L, int deep) {
19     int l = 0, r = c - 1, mid;
20     while (l < r) {
21         mid = (l + r) >> 1;
22         if (sta[mid] < deep - L + 1) {
23             l = mid + 1;
24         } else {
25             r = mid;
26         }
27     }
28     return ar[sta[l]];
29 }
30 int main() {
31     int u, t;
32     while (2 == scanf("%d%d", &m, &d)) {
33         c = cc = t = 0;
34         for (int i = 1; i <= m; ++i) {
35             scanf("%s%d", s, &u);
36             if (*s == 'A') {
37                 u = (u + t) % d;
38                 ar[++cc] = u;
39                 while (c != 0 && ar[sta[c - 1]] < u) --c;
40                 sta[c ++] = cc;
41             } else if (*s == 'Q') {
42                 t = bSearch(u, cc);
43                 printf("%d\n", t);
44             }
45         }
46     }
47     return 0;
48 }
View Code 


1047:

要在一个1000*1000的矩形中找到一个正方形,使得这个正方形中的最大值-最小值最小。

首先我们可以用单调队列维护

rmax[a][b]//从(a,b)这个点向右连续n个点的最大值

rmin[a][b]//从(a,b)这个点向右连续n个点的最小值

复杂度是O(A*B)

然后

qmax[a][b]//以(a,b)为左上角的矩形中的最大值

就等于max(rmax[a][b], .....rmax[a + x - 1][b]);

那么我们就将二维问题变成了一维问题,这个也用单调队列扫一遍就好了

  1 /**************************************************************
  2     Problem: 1047
  3     User: pikapika
  4     Language: C++
  5     Result: Accepted
  6     Time:2356 ms
  7     Memory:20764 kb
  8 ****************************************************************/
  9  
 10 #include <cstdio>
 11 #include <algorithm>
 12 #include <cstring>
 13 #include <queue>
 14  
 15 struct node {
 16     int x, y, v;
 17     node(int _x, int _y, int _v) {
 18         x = _x, y = _y, v = _v;
 19     }
 20     node() {}
 21 };
 22 std::deque<node> Max, Min;
 23  
 24 const int N = 1000 + 10;
 25  
 26 int ar[N][N];
 27 int r, c, s;
 28  
 29 int ans;
 30 int rmax[N][N], rmin[N][N];
 31 int qmax[N][N], qmin[N][N];
 32  
 33 void update_max(int& z, int v) {
 34     if (-1 == z) {
 35         z = v;
 36     } else {
 37         z = std::max(z, v);
 38     }
 39 }
 40 void update_min(int& z, int v) {
 41     if (-1 == z) {
 42         z = v;
 43     } else {
 44         z = std::min(z, v);
 45     }
 46 }
 47 void prepare() {
 48     ans = (int)(1e9) + 10;
 49     //////////////////////////////////
 50     memset(rmax, -1sizeof(rmax));
 51     memset(rmin, -1sizeof(rmin));
 52     for (int i = 1; i <= r; ++i) {
 53         while (!Max.empty()) Max.pop_back();
 54         while (!Min.empty()) Min.pop_back();
 55  
 56         for (int j = 1; j < s; ++j) {
 57             while (!Max.empty() && Max.back().v < ar[i][j]) {
 58                 Max.pop_back();
 59             }
 60             Max.push_back(node(i, j, ar[i][j]));
 61             while (!Min.empty() && Min.back().v > ar[i][j]) {
 62                 Min.pop_back();
 63             }
 64             Min.push_back(node(i, j, ar[i][j]));
 65         }
 66  
 67         for (int j = s; j <= c; ++j) {
 68             while (!Max.empty() && Max.back().v < ar[i][j]) {
 69                 Max.pop_back();
 70             }
 71             Max.push_back(node(i, j, ar[i][j]));
 72             while (!Min.empty() && Min.back().v > ar[i][j]) {
 73                 Min.pop_back();
 74             }
 75             Min.push_back(node(i, j, ar[i][j]));
 76             while (!Max.empty() && Max.front().y <= j - s)
 77                 Max.pop_front();
 78             while (!Min.empty() && Min.front().y <= j - s)
 79                 Min.pop_front();
 80  
 81             update_max(rmax[i][j - s + 1], Max.front().v);
 82             update_min(rmin[i][j - s + 1], Min.front().v);
 83 //            printf("%d*****%d\n", Max.front().v, rmax[i][j - s + 1]);
 84         }
 85     }
 86  
 87     memset(qmax, -1sizeof(qmax));
 88     memset(qmin, -1sizeof(qmin));
 89  
 90     for (int j = 1; j + s - 1 <= c; ++j) {
 91         while (!Max.empty()) Max.pop_back();
 92         while (!Min.empty()) Min.pop_back();
 93  
 94         for (int i = 1; i < s; ++i) {
 95             while (!Max.empty() && Max.back().v < rmax[i][j]) {
 96                 Max.pop_back();
 97             }
 98             Max.push_back(node(i, j, rmax[i][j]));
 99             while (!Min.empty() && Min.back().v > rmin[i][j]) {
100                 Min.pop_back();
101             }
102             Min.push_back(node(i, j, rmin[i][j]));
103         }
104  
105         for (int i = s; i <= r; ++i) {
106             while (!Max.empty() && Max.back().v < rmax[i][j]) {
107                 Max.pop_back();
108             }
109             Max.push_back(node(i, j, rmax[i][j]));
110             while (!Min.empty() && Min.back().v > rmin[i][j]) {
111                 Min.pop_back();
112             }
113             Min.push_back(node(i, j, rmin[i][j]));
114             while (!Max.empty() && Max.front().x <= i - s)
115                 Max.pop_front();
116             while (!Min.empty() && Min.front().x <= i - s)
117                 Min.pop_front();
118             update_max(qmax[i - s + 1][j], Max.front().v);
119             update_min(qmin[i - s + 1][j], Min.front().v);
120         }
121     }
122 }
123 void work() {
124     for (int i = 1; i <= r; ++i)
125         for (int j = 1; j <= c; ++j)
126             scanf("%d", &ar[i][j]);
127  
128     prepare();
129  
130     for (int i = 1; i + s - 1 <= r; ++i) {
131         for (int j = 1; j + s - 1 <= c; ++j) {
132 //            printf("%d %d\n", qmax[i][j], qmin[i][j]);
133             ans = std::min(ans, qmax[i][j] - qmin[i][j]);
134         }
135     }
136     printf("%d\n", ans);
137 }
138  
139 int main() {
140     while (3 == scanf("%d%d%d", &r, &c, &s)) {
141         work();
142     }
143     return 0;
144 }
View Code 


/////////////////////////////////////////

四、贪心


1029:

参见论文《浅谈信息学竞赛中的区间问题》(链接 

http://wenku.baidu.com/view/fcb4dd88d0d233d4b14e6925.html )

首先我们将所有事件按照结束时间从小到大排序。//理由很显然吧

然后就是一个分阶段的问题,假设当前已经处理完了前i - 1个事件,取了k个事件,总时间是d[i],如果此时还可以选取事件i,那么最优方案显然是选取事件i。

如果不能选取事件i,那么我们已经无法增大k了,但是我们还有可能减小d[i],如果第i个事件解决所需要的时间小于已经取了的k个事件的最大值,那么可以用第i个事件替换那个事件。

用一个优先队列维护就好了。

 1 /**************************************************************
 2     Problem: 1029
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:416 ms
 7     Memory:2752 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <queue>
12 #include <algorithm>
13  
14 typedef std::pair<intint>pii;
15 const int N = 150000 + 10;
16 pii ar[N];
17  
18 std::priority_queue<pii>Q;
19 int n;
20  
21 bool cmp(const pii& a, const pii& b) {
22     return a.second < b.second;
23 }
24 void work() {
25     while (!Q.empty()) Q.pop();
26  
27     for (int i = 0; i < n; ++i)
28             scanf("%d%d", &ar[i].first, &ar[i].second);
29  
30     std::sort(ar, ar + n, cmp);
31  
32     int now = 0;
33     for (int i = 0; i < n; ++i) {
34         if (now + ar[i].first <= ar[i].second) {
35             Q.push(ar[i]);
36             now += ar[i].first;
37         } else if (!Q.empty() && Q.top().first >= ar[i].first) {
38             now = now + ar[i].first - Q.top().first;
39             Q.pop();
40             Q.push(ar[i]);
41         }
42     }
43  
44     printf("%d\n", Q.size());
45 }
46 int main() {
47     while (1 == scanf("%d", &n)) {
48         work();
49     }
50     return 0;
51 }
View Code 


1034:

经典的田忌赛马。

参加( http://oibh.info/archives/261 )

 1 /**************************************************************
 2     Problem: 1034
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:252 ms
 7     Memory:1588 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12  
13 const int N = 100000 + 10;
14 int a[N], b[N];
15  
16 int n;
17  
18 int solve(int* a, int* b) {
19     int re = 0;
20     int al, ar, bl, br;
21     al = bl = 0;
22     ar = br = n - 1;
23     while (ar >= al) {
24         if (a[ar] > b[br]) {
25             --ar; --br;
26             re += 2;
27         } else if (a[al] > b[bl]) {
28             ++al; ++bl;
29             re += 2;
30         } else if (a[al] == b[br]) {
31             ++al; --br;
32             re += 1;
33         } else {
34             ++al; --br;
35         }
36     }
37     return re;
38 }
39 void work() {
40     for (int i = 0; i < n; ++i)
41         scanf("%d", &a[i]);
42     for (int j = 0; j < n; ++j)
43         scanf("%d", &b[j]);
44  
45     std::sort(a, a + n);
46     std::sort(b, b + n);
47  
48     printf("%d %d\n", solve(a, b), 2 * n - solve(b, a));
49 }
50 int main() {
51     while (1 == scanf("%d", &n)) {
52         work();
53     }
54     return 0;
55 }
View Code 

 

///////////////////////////////////

五、数据结构

1、并查集


1015:

倒着做并查集

 1 /**************************************************************
 2     Problem: 1015
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:1500 ms
 7     Memory:12600 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <iostream>
12 #include <cstring>
13 #include <vector>
14 #include <algorithm>
15 using namespace std;
16 const int M = 200020;
17 const int N = M << 1;
18 struct Edge {
19     int u, v, nex;
20 };
21 int fa[N], cnt, n, m;
22 int g[N], idx, ar[N], c, ans[N];
23 Edge eg[N];
24 bool can[N];
25  
26 int find(int x) {
27     if (fa[x] == x) return fa[x];
28     else return fa[x] = find(fa[x]);
29 }
30 void Union(int x, int y) {
31     int xx = find(x), yy = find(y);
32     if (xx == yy) return ;
33     --cnt; fa[xx] = yy;
34 }
35 void addedge(int u, int v) {
36     eg[idx].u = u, eg[idx].v = v, eg[idx].nex = g[u];
37     g[u] = idx++;
38 }
39 void input() {
40     for (int i = 0; i < n; ++i) 
41         fa[i]  = i;
42     cnt = n; idx = 0;
43     memset(can, 0sizeof(can));
44     memset(g, -1sizeof(g));
45     int u, v;
46     for (int i = 0; i < m; ++i) {
47         scanf("%d%d", &u, &v);
48         addedge(u, v);
49     }
50     scanf("%d", &c);
51     for (int i = 0 ; i < c; ++i) {
52         scanf("%d", &ar[i]);
53         can[ar[i]] = true;
54     }
55 }
56 void solve() {
57     for (int i = 0; i < n; ++i) {
58         if (can[i]) continue;
59         for (int j = g[i]; ~j; j = eg[j].nex) {
60             if (can[eg[j].v]) addedge(eg[j].v, eg[j].u);
61             else Union(eg[j].u, eg[j].v);
62         }
63     }
64     int cc = 0;
65     ans[cc++] = cnt - c;
66     for (int i = c - 1; i >= 0; --i) {
67         can[ar[i]] = false;
68         for (int j = g[ar[i]]; ~j; j = eg[j].nex) {
69             if (can[eg[j].v]) addedge(eg[j].v, eg[j].u);
70             else Union(eg[j].u, eg[j].v);
71         }
72         ans[cc++] = cnt - i;
73     }
74     for (int i = cc - 1; i >= 0; --i)
75         printf("%d\n", ans[i]);
76 }
77 int main() {
78     while (2 == scanf("%d%d", &n, &m)) {
79         input();
80         solve();
81     }
82     return 0;
83 }
View Code 


六、数学

1、计数问题


1005:

 参见( http://hi.baidu.com/aekdycoin/item/589a99d3fad167352b35c7d4 )

首先要知道某树可以唯一表示为一个长度为 n - 2 的序列

/*

我们解释下红字部分, 原理叫做普吕弗(Prüfer)序列一棵树要得到普吕弗序列,方法是逐次去掉树的顶点,直到剩下两个顶点。考虑树T,其顶点为{1, 2, ..., n}。在第i步,去掉标号最小的叶,并把普吕弗序列的第i项设为这叶的邻顶点的标号。

一棵树的序列明显是唯一的,而且长为n − 2。

从一个普吕弗序列,可以求得一棵树有这一普吕弗序列。

已知序列还原树的算法:

设这普吕弗序列长n − 2。首先写出数1至n。第一步,找出1至n中没有在序列中出现的最小数。把标号为这数的顶点和标号为序列首项的顶点连起来,并把这数从1至n中删去,序列的首项也删去。接着每一步以1至n中剩下的数和余下序列重复以上步骤。最后当序列用完,把1至n中最后剩下的两数的顶点连起来。

*/

,其中每个点出现的次数=树中度数-1并且可以根据序列唯一的构造出一颗树
于是问题就转化为:对于给定的n-2个位置来安排点.使得满足所有条件
先考虑那些有限制的点,假设有 Tot  个(Tot <= n,很显然)
那么显然就一组合数学中的有重复元素的排列问题:
从n-2 个位置中选出来Tot个,然后进行有重复元素的全排列


其他的位置显然每一个位置都有(n-w)种可能,于是答案出来了


/********************************/

 既然是高精度我就直接上java
 1 /**************************************************************
 2     Problem: 1005
 3     User: pikapika
 4     Language: Java
 5     Result: Accepted
 6     Time:1328 ms
 7     Memory:19132 kb
 8 ****************************************************************/
 9  
10 import java.io.BufferedInputStream;
11 import java.math.BigInteger;
12 import java.util.Scanner;
13  
14 public class Main {
15     public static BigInteger Cal(int n) {
16         BigInteger re = BigInteger.ONE;
17         for (int i = 2; i <= n; ++i)
18             re = re.multiply(BigInteger.valueOf(i));
19         return re;
20     }
21     public static void main(String[] args) {
22         Scanner cin = new Scanner(new BufferedInputStream(System.in));
23         int ar[] = new int[1001];
24         int c = 0, k, sum = 0;
25         int n = cin.nextInt();
26         for (int i = 0; i < n; ++i) {
27             k = cin.nextInt();
28             if (-1 == k) continue;
29             else {
30                 sum += k - 1;
31                 ar[c++] = k - 1;
32             }
33         }
34         BigInteger ans = Cal(sum);
35         for (int i = 0; i < c; ++i)
36             ans = ans.divide(Cal(ar[i]));
37         ans = ans.multiply(BigInteger.valueOf(n - c).pow(n - 2 - sum));
38         ans = ans.multiply(Cal(n - 2)).divide(Cal(sum)).divide(Cal(n - 2 - sum));
39         System.out.println(ans);
40     }
41  
42 }
View Code

 

1008:

题目可以看成n个格子,每个格子可以染1到m任意一个颜色,问有多少种情况存在任意两个相邻格子同色。

首先我们知道总共的染色方案是m^n次,那么我们只要反过来求有多少种不越狱的情况就可以了。

第一个格子有m种染色方案,后面的每个格子都不可以和前面一个格子同色,即m-1种方案,所以答案就是m^n - m * (m - 1) ^ (n - 1)

用快速幂可以迅速求出答案

 1 /**************************************************************
 2     Problem: 1008
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:0 ms
 7     Memory:1272 kb
 8 ****************************************************************/
 9  
10 #include <iostream>
11 #include <cstdio>
12 #include <algorithm>
13 #include <cstring>
14 using namespace std;
15 #define typ unsigned long long
16 const typ MOD = 100003;
17 typ n, m;
18  
19 typ Pow(typ x, typ y) {
20     typ re = 1;
21     while (y > 0) {
22         if (y % 2 == 1)
23             re = (re * x) % MOD;
24         x = (x * x) % MOD;
25         y = y / 2;
26     }
27     return re;
28 }
29 int main() {
30     while (cin >> m >> n) {
31         typ ans = (Pow(m, n) - (m * Pow(m - 1, n - 1)) % MOD + MOD) % MOD;
32         cout << ans << endl;
33     }
34     return 0;
35 }
36 
View Code


2、数学分析

 

1045:

[HAOI2008] 糖果传递

这是一道陈题,原题是uva 11300

具体分析参见( http://www.cnblogs.com/kuangbin/archive/2012/10/24/2736733.html )

刘汝佳的《算法竞赛——入门经典》中第一章1.1 例题3(p4)中有具体分析

 1 /**************************************************************
 2     Problem: 1045
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:2712 ms
 7     Memory:16432 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12  
13 typedef long long ll;
14 const int N = 1000000 + 10;
15 ll a[N], c[N];
16 int n;
17  
18 void work() {
19     ll tot = 0;
20     for (int i = 1; i <= n; ++i) {
21         scanf("%lld", &a[i]);
22         tot += a[i];
23     }
24     ll avg = tot / n;
25     c[1] = 0;
26     for (int i = 2; i <= n; ++i) {
27         c[i] = c[i - 1] + avg - a[i - 1];
28     }
29  
30     std::sort(c + 1, c + 1 + n);
31     ll mid = c[(n + 1) >> 1];
32     ll ans = 0;
33     for (int i = 1; i <= n; ++i)
34         ans = ans + abs(c[i] - mid);
35  
36     printf("%lld\n", ans);
37 }
38 int main() {
39     while (1 == scanf("%d", &n)) {
40         work();
41     }
42     return 0;
43 }
View Code 

 

七、博弈

 

1022:

经典的ANTI-NIM博弈问题

在anti-Nim游戏中,先手必胜当且仅当:

(1)所有堆的石子数都为1且游戏的SG值为0;

(2)有些堆的石子数大于1且游戏的SG值不为0。

 参见2009年国家集训队论文贾志豪论文《组合游戏略述 ——浅谈SG游戏的若干拓展及变形》

( http://wenku.baidu.com/view/25540742a8956bec0975e3a8.html )

 1 /**************************************************************
 2     Problem: 1022
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:40 ms
 7     Memory:1272 kb
 8 ****************************************************************/
 9  
10 #include <iostream>
11 #include <cstdio>
12 #include <algorithm>
13 #include <queue>
14 #include <cstring>
15 using namespace std;
16  
17 int main() {
18     int T, v, g, n, k;
19     scanf("%d", &T);
20     while (T--) {
21         v = g = 0;
22         scanf("%d", &n);
23         for (int i = 1; i <= n; ++i) {
24             scanf("%d", &k);
25             if (k == 1) ++ g;
26             v ^= k;
27         }
28         if (g == n) {
29             if (v == 0)
30                 puts("John");
31             else puts("Brother");
32         } else {
33             if (v > 0
34                 puts("John");
35             else puts("Brother");
36         }
37     }
38     return 0;
39 }
View Code 


////////////////////////

八、搜索


1024:

搜索

首先,要求分割出来的n块面积都一样,我们枚举当前矩形被切成两块时这两块矩形还将在以后被切几刀,于是相应的边长也被分成了对应比例。

 1 /**************************************************************
 2     Problem: 1024
 3     User: pikapika
 4     Language: C++
 5     Result: Accepted
 6     Time:184 ms
 7     Memory:804 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <algorithm>
12  
13 const double INF = 1e100;
14  
15 double dfs(double x, double y, int c) {
16     if (y > x) std::swap(x, y);
17     if (1 == c) return x / y;
18     else {
19         double re = INF;
20         for (int i = 1; i < c; ++i) {
21             re = std::min(re, std::max(dfs(x, y * i * (1. / c), i), dfs(x, y - y * i * (1. / c), c - i)));
22         }
23         for (int i = 1; i < c; ++i) {
24             re = std::min(re, std::max(dfs(x * i * (1. / c), y, i), dfs(x - x * i * (1. / c), y, c - i)));
25         }
26         return re;
27     }
28 }
29 int main() {
30     double x, y;
31     int c;
32     while (3 == scanf("%lf%lf%d", &x, &y, &c)) {
33         printf("%.6f\n", dfs(x, y, c));
34     }
35     return 0;
36 }
View Code

 

 

 

posted @ 2013-09-08 10:03  masterhe  阅读(4292)  评论(0编辑  收藏  举报