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

目录:

一、DP

二、图论

  1、最短路

  2、强连通分量

三、利用单调性维护

四、贪心

五、数据结构

  1、并查集

六、数学

  1、计数问题

  2、数学分析 

七、博弈

八、搜索

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

 

一、DP:


1003 : 

[ZJOI2006]物流运输trans

(参见 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
ExpandedBlockStart.gif
  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 
 11 #include 
 12 #include < string>
 13 #include 
 14 #include 
 15 #include 
 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:

[HNOI2008]GT考试

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

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

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

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
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:
[HNOI2008]玩具装箱toy

首先是一个很明显的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 )

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
14 #include 
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 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 )

ExpandedBlockStart.gif
 1 #include 
 2 #include 
 3 #include 
 4 #include 
 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:

[SHOI2008]Debt 循环的债务

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

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

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
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:

[SCOI2009]游戏

(参见 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)

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
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)

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
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:

[ZJOI2008]生日聚会Party

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个是女生

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
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) <= 第一问的结果的值

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

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
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:

[HAOI2007]上升序列

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
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:

[HAOI2007]分割矩阵
记忆化搜索 

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
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:

[HAOI2006]数字序列

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

我们可以先读入每一个数,然后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位的值

ExpandedBlockStart.gif
 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 
11 #include 
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判重,可以直接过,但是速度不够快。

ExpandedBlockStart.gif
 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 
11 #include 
12 #include < set>
13 #include 
14 typedef  long  long ll;
15  const  int N =  10 +  3;
16  
17 std:: set 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个数 

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
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:

[SCOI2005]最大子矩阵

 我们注意到列最大只有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个矩阵

ExpandedBlockStart.gif
  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 
 11 #include 
 12 #include 
 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 )

ExpandedBlockStart.gif
  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 
 11 #include 
 12 #include 
 13 #include 
 14 #include 
 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:

[HAOI2006]旅行comf

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

ExpandedBlockStart.gif
  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 
 11 #include 
 12 #include 
 13 #include 
 14 #include 
 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:

[HAOI2006]受欢迎的牛

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

ExpandedBlockStart.gif
  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 
 11 #include 
 12 #include 
 13 #include 
 14 #include 
 15 #include 
 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:

[JSOI2008]最大数maxnumber

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

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
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:

[HAOI2007]理想的正方形

要在一个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]);

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

ExpandedBlockStart.gif
  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 
 11 #include 
 12 #include 
 13 #include 
 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 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:

[JSOI2007]建筑抢修

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

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

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

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

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

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13  
14 typedef std::pair< intint>pii;
15  const  int N =  150000 +  10;
16 pii ar[N];
17  
18 std::priority_queueQ;
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:

[ZJOI2008]泡泡堂BNB

经典的田忌赛马。

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

ExpandedBlockStart.gif
 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 
11 #include 
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:

[JSOI2008]星球大战starwar

倒着做并查集

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
14 #include 
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:

[HNOI2008]明明的烦恼

 参见( 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个,然后进行有重复元素的全排列

[bzoj\lydsy\大视野在线测评]题解(持续更新)_第1张图片

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

[bzoj\lydsy\大视野在线测评]题解(持续更新)_第2张图片

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

 既然是高精度我就直接上java
ExpandedBlockStart.gif
 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:

[HNOI2008]越狱

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

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

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

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
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)中有具体分析

ExpandedBlockStart.gif
 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 
11 #include 
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:

[SHOI2008]小约翰的游戏John

经典的ANTI-NIM博弈问题

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

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

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

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

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

ExpandedBlockStart.gif
 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 
11 #include 
12 #include 
13 #include 
14 #include 
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:

[SCOI2009]生日快乐

搜索

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

ExpandedBlockStart.gif
 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 
11 #include 
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

 

 

 

转载于:https://www.cnblogs.com/hewifi/p/3307793.html

你可能感兴趣的:([bzoj\lydsy\大视野在线测评]题解(持续更新))