[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

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM