目录:

一、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)
;
;
; i <= tot; ++i)
, ;
 << eg[i].v))
;
, m, m);
);
 <= r; ++i) {
, r) + kkcld, z);
, , ;
; i < e; ++i) {
     ; i < d; ++i) {
         scanf(                      ar[j] |= ( << p);
         }
     }
 }
      memset(d, -,      printf(, n));
 }
       == scanf(         input();
         solve();
     }
     ;
 }

1009:

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

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

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

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

 #include <iostream>
 #include <cstdio>
 #include <algorithm>
 #include <cstring>
  ;
                         memset(val, ,      }
              ; i < r; ++i)
             val[i][i] = ;
     }
 };
  
 Matrix M, ans;
    
 Matrix multi(Matrix a, Matrix b) {
     Matrix re;
     re.r = a.r, re.c = b.c;
     re.clear();
     ; i < re.r; ++i) 
         ; j < re.c; ++j) 
             ; k < re.c; ++k) 
                 re.val[i][j] = (re.val[i][j] + (a.val[i][k] * b.val[k][j]) % mod) % mod;
      }
 Matrix Pow(Matrix a,      Matrix re;
     re.clear();
     re.r = re.c = a.r;
     re.One();
              ) re = multi(re, a);
         a = multi(a, a);
         x >>= ;
     }
      }
           f[] = f[] = ;
     M.r = m, M.c = m;
     M.clear();
     ; i < m; ++i) {
         j = f[i];
                  ] = j + ;
         ] = ;
     }
     ; i < m; ++i) {
         ; j < ; ++j) {
             p = i;
                          )              ];
                      }
     }
     ans.r = , ans.c = m;
     ans.clear();
     ans.val[][] = ;
 }
            == scanf(         ; i < m; ++i)
             scanf(         prepare();
         tot = ;
         M = Pow(M, n);
         ans = multi(ans, M);
         ; i < m; ++i)
             tot = (tot + ans.val[][i]) % mod;
         printf(  
     }
     ;
 }
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 )

 #include <iostream>
 #include <cstdio>
 #include <algorithm>
 #include <cstring>
 #include <queue>
   ;
           node() {
     }
     node(         l = _l, r = _r, id = _id;
     }
 };
  
 deque<node> Q;
 typ d[N], sum[N], L, c[N];
   
      ; i <= n; ++i)
         scanf(     sum[] = ;
     ; i <= n; ++i) 
         sum[i] = sum[i - ] + c[i];
 }
 typ sqr(typ x) {
      }
 typ Cal(     ] + r - l - L);
 }
      node u;
              Q.pop_back();
     Q.push_front(node(, n, ));
     d[] = ;
     ; i <= n; ++i) {
         u = Q.front();
         d[i] = d[u.id] + Cal(u.id + , i);
                                                                  Q.push_back(node(i + , n, i));
                              }
             u = Q.back();
             , u.l) >= d[i] + Cal(i + , u.l)) {
                 Q.pop_back();
                              }
             , u.r) <= d[i] + Cal(i + , u.r)) {
                                      Q.push_back(node(u.r + , n, i));
                              }
                                                        mid = (L + R) >> ;
                 , mid) < d[i] + Cal(i + , mid)) {
                     L = mid + ;
                 }                      R = mid;
                 }
             }
             Q.back().r = L - ;
             Q.push_back(node(L, n, i));
                      }
     }
     printf( }
       == scanf(         input();
         solve();
     }
     ;
 }

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

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

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

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

;
 deque<  
      sum[] = ;
     ; i <= n; ++i) {
         scanf(         sum[i] += sum[i - ];
     }
 }
 LL A(      }
 LL B(      }
 LL sqr(LL x) {
      }
 LL Up(      }
 LL Down(      }
       }
 LL Cal(      + sum[j] - sum[i] - L);
 }
                    Q.pop_back();
     d[] = ;
     Q.push_back();
     ; i <= n; ++i) {
         ) {
             u = Q.front();
             Q.pop_front();
             v = Q.front();
                              Q.push_front(u);              }
         }
         u = Q.front();
         d[i] = Cal(u, i);
                  ) {
             v = Q.back();
             Q.pop_back();
             u = Q.back();
                                           Q.push_back(v);              }
         }
         Q.push_back(i);
     }
     printf( }
       == scanf(         input();
         solve();
     }
     ;
 }

1021:

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

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

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

 #include <cstdio>
 #include <algorithm>
 #include <cstring>
  
 ;
  
 ;
 ][N];
  
  ], mon[N];
  
  + ;
 ][MAX][MAX];
  
 , , , , , };
  
       == x)
         x = y;
              x = std::min(x, y);
 }
      memset(mon, ,   
     ;
     ; i < ; ++i) {
         sum[i] = ;
         ; j >= ; --j) {
             scanf(             mon[j] += own[i][j];
             sum[i] += own[i][j] * val[j];
         }
         tot += sum[i];
     }
  
     memset(d[], -, ]));
     d[][sum[]][sum[]] = ;
     ; i < N; ++i) {
         ;
         memset(d[ - now], -, ]));
  
         ; j <= tot; ++j) {
             ; k + j <= tot; ++k) {
                 ) {
                     update(d[ - now][j][k], d[now][j][k]);
  
                     ; a <= mon[i]; ++a) {
                         ; b + a <= mon[i]; ++b) {
                             ][i]);
                             ][i]);
                              && sumb >=  && suma + sumb <= tot) {
                                 ][i]) + std::abs(b - own[][i]) + std::abs(mon[i] - a - b - own[][i])) / ;
                                 update(d[ - now][suma][sumb], d[now][j][k] + dis);
                             }
                         }
                     }
                 }
             }
         }
     }
     ], eb = sum[], ec = sum[];
     ea -= x1; eb += x1;
     eb -= x2; ec += x2;
     ec -= x3; ea += x3;
      || eb <  || ec <  || ea + eb + ec != tot || d[N & ][ea][eb] < )
         puts(              printf(][ea][eb]);
 }
       == scanf(         work();
     }
     ;
 }

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)

 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 #include <vector>
 typedef   
  + ;
 ll d[N][N];
   
  std::vector<  
      prime.clear();
     memset(vis, ,      ; i <= n; ++i) {
                      prime.push_back(i);
                              vis[j] =          }
     }
 }
       == scanf(         prepare();
  
         memset(d, ,          d[][] = ;
         ; i < prime.size(); ++i) {
             ; j <= n; ++j) {
                 )                  d[i + ][j] += d[i][j];
                                      d[i + ][j + z] += d[i][j];
                 }
             }
         }
  
         ll ans = ;
         ; i <= n; ++i)
             ans += d[prime.size()][i];
         printf(     }
     ;
 }

1026:

[SCOI2009]windy数

数位dp:

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

 #include <cstdio>
 #include <algorithm>
 #include <cstring>
 #include <vector>
  
  + ;
   
            == len) {
         ;
     }
     d[x][len] = ;
     ; i < ; ++i)
         d[x][len] += dp(i, len - );
     ; i >= ; --i) {
         d[x][len] += dp(i, len - );
     }
      }
  std::vector<  
      ) {
             ;
         ; i <= ar[len - ]; ++i)
             ++re, ++z;
         , ar[len - ]); i >= ; --i)
             ++re, ++z;
     }          , ar[len - ]); i >= ; --i) {
             ]) {
                 dfs(re, len - );
             }                  re += dp(i, len);
             }
         }
         ; i <= ar[len - ]; ++i) {
             ]) {
                 dfs(re, len - );
             }                  re += dp(i, len);
             }
         }
     }
 }
      ) ;
      == x) ;
  
     ar.clear();
     ;
              ar.push_back(x % );
         x /= ;
     }
  
      == ar.size())
         ] + ;
  
          ; i < ; ++i)
         ;  j < len; ++j)
             re += dp(i, j);
     ++re;
     ; i <= ar[len - ]; ++i) {
         ]) {
             dfs(re, len - );
         }              re += dp(i, len);
         }
     }
      }
      );
          printf( }
      memset(d, -,   
      == scanf(         work();
     }
     ;
 }

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

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

 #include <cstdio>
 #include <algorithm>
 #include <cstring>
  
 ;
  
   
  + ;
  + ;
 ][M][N][N];
  
      memset(d, ,      d[][][][] = ;
  
     ; i <= (n + m); ++i) {
         , pre =  - now;
         memset(d[now], , ]));
  
         ; j <= std::min(i, n); ++j)
         ; k1 <= std::min(i, lim); ++k1) {
             ; k2 <= std::min(lim, i - j); ++k2) {
                 ][k1 + ][std::max(, k2 - )];
                 z = (z + d[pre][j][k1][k2]) % mod;
                 , k1 - )][k2 + ];
                 zz = (zz + d[pre][j][k1][k2]) % mod;
             }
         }
     }
  
     ;
     ; k1 <= lim; ++k1)
         ; k2 <= lim; ++k2)
             sum = (sum + d[(n + m) & ][n][k1][k2]) % mod;
     printf( }
  
       == scanf(         work();
     }
     ;
 }

1044:

[HAOI2008]木棍分割

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

第二问dp做:

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

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

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

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

 #include <cstdio>
 #include <algorithm>
 #include <cstring>
 ;
  + ;
  
 ][N];
  
   
      , tot = ;
     ; i <= n; ++i) {
         tot = tot + ar[i];
                      ++re;
             tot = ar[i];
         }
     }
           }
      sum[] = ar[] = ;
     ; i <= n; ++i) {
         scanf(         sum[i] = sum[i - ] + ar[i];
     }
          l = ; r = sum[n];
     ) {
         mid = (l + r) / ;
                      r = mid;
                      l = mid;
     }
     printf(  
          memset(d[], , ]));
     ; i <= n; ++i)
                      ++d[][i];
                        
          , a = d[][n];
  
     ; j <= m; ++j) {
         , pre =  - now;
         memset(d[now], ,   
         presum = tot = ;
         idx = ;
  
         ; i <= n; ++i) {
             tot = tot + ar[i];
             presum = (presum + d[pre][i - ]) % mod;
  
                              tot = tot - ar[idx + ];
                 presum = (presum - d[pre][idx] + mod) % mod;
                 ++idx;
             }
  
             d[now][i] = presum;
         }
         a = (a + d[now][n]) % mod;
     }
  
     printf( }
       == scanf(         work();
     }
     ;
 }

1046:

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

 #include <cstdio>
 #include <algorithm>
 #include <cstring>
 #include <vector>
  
 ;
  + ;
   
           l = ,r = n;
     ) {
         mid = (l + r) >> ;
                      r = mid;
                      l = mid;
     }
     g[r] = val;
      }
 std::vector<  
      ; i <= n; ++i)
         scanf(     std::fill(g, g + n + , -INF);
     ;
     ; i <= n; ++i) {
         d[n - i + ] = bsearch(ar[n - i + ]);
         mx = std::max(mx, d[n +  - i]);
     }
  
          scanf(              scanf(                      puts(                      ans.clear();
             ans.push_back(-INF);
             ; i <= n; ++i) {
                 ]) {
                     ans.push_back(ar[i]);
                     )                  }
             }
             ; i < ans.size(); ++i) {
                 )
                     putchar(                 printf(             }
             putchar(         }
     }
 }
       == scanf(         work();
     }
     ;
 }

1048:

记忆化搜索 

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

 #include <cstdio>
 #include <algorithm>
 #include <cstring>
 #include <cmath>
 ;
  
  + ;
     
       ;
                           re += ar[i][j];
      }
                 == ti) {
              }
     z = INF;
              ; j < ti; ++j)
             z = std::min(z, dp(x1, y1, i, y2, j) + dp(i + , y1, x2, y2, ti - j));
              ; j < ti; ++j)
             z = std::min(z, dp(x1, y1, x2, i, j) + dp(x1, i + , x2, y2, ti - j));
     }
      }
      ;
     ; i <= r; ++i)
         ; j <= c; ++j) {
             scanf(             tot += ar[i][j];
         }
     memset(d, -,   
     , , r, c, lim);
                . * avg * tot / lim + avg * avg);
     printf( }
       == scanf(         work();
     }
     ;
 }

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位的值

 #include <cstdio>
 #include <algorithm>
       };
  
  + ;
 node d[N];
  
    
 inline      ) {
                      a.val = nval;
             a.cos = ncos;
         }              a.cos = std::min(a.cos, ncos);
         }
     }          a.val = nval;
         a.cos = ncos;
     }
 }
      ; i <= n; ++i) {
         scanf(         ar[i] = ar[i] - i;
         x[i - ] = ar[i];
     }
  
     std::sort(x, x + n);
       
     ; i < idx; ++i)
         d[i].cos = d[i].val = ;
  
     ; i <= n; ++i) {
             ; j < idx; ++j) {
                 ) {
                                              d[j].val = d[j].val + ;
                     d[j].cos = d[j].cos + std::abs(ar[i] - x[j]);
                 }
                  && d[j - ].val >= ) {
                     update(d[j], d[j - ].val, d[j - ].cos);
                 }
       }
  
     node ans;
     ans.val = -; ans.cos = ;
     ; i < idx; ++i)
         )
             update(ans, d[i].val, d[i].cos);
     printf( }
       == scanf(         work();
     }
     ;
 }

1072:

[SCOI2007]排列perm

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

 #include <cstdio>
 #include <algorithm>
 #include < #include <cstring>
 typedef   + ;
  
 std::  
    
           v.clear();
  
     scanf(  
     ;
     ; s[i]; ++i) {
         ar[idx ++] = s[i] -      }
  
     ll ans = ;
  
     std::sort(ar, ar + idx);
              ll num = ;
         ; i < idx; ++i) {
             num = num *  + ar[i];
         }
                  ) {
             ++ans;
             v.insert(num);
         }
     }   
     printf( }
           scanf(     ) {
         work();
     }
     ;
 }

比较好的方法是状压DP,

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

我们有转移方程

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

 #include <cstdio>
 #include <algorithm>
 #include <cstring>
 typedef   + ;
  
    
 ll dp[ << ][N];
  
           scanf(  
     ;
     ; ch[i]; ++i) {
         ar[idx ++] = ch[i] -      }
  
     ll ans = , tmp = ;
     std::sort(ar, ar + idx);
  
     ; i < idx;) {
         ;
                                                    i = j;              }
              == idx) i = idx;
         }
         ; j <= cnt; ++j) {
             tmp = tmp * j;
         }
     }
  
     memset(dp, ,      dp[][] = ;
  
      << idx;
     ; i < idx; ++i) {
         ) {
             ; k < idx; ++k)
                 dp[ << k][ar[k] % d] = ;
  
         }               << i) - ;
                              ; j < d; ++j) {
                     )
                         ; k < idx; ++k) {
                             )) {
                                 dp[s | ( << k)][(j *  + ar[k]) % d] += dp[s][j];
                             }
                         }
                 }
                                                   s = ((s & ~y) / x >> ) | y;
             }
         }
     }
     printf(][] / tmp);
 }
           scanf(     ) {
         work();
     }
     ;
 }

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个矩阵

;
 + ;
 + ;
 + ;
;
; i <= r; ++i)
; j <= c; ++j)
 == c) {
; i <= r; ++i)
; j <= lim; ++j)
][] = ;
; i < r; ++i) {
; j <= lim; ++j) {
; k <= r; ++k) {
], rd[i][j] + sum(i + , , k, ));
 == lim) update(ans, rd[k][j + ]);
; i <= r; ++i)
; j <= r; ++j)
; k <= lim; ++k)
][][] = ;
; i <= r; ++i)
; j <= r; ++j) {
; k <= lim; ++k) {
, j + );
], d[i][j][k] + sum(z, , to, ));
 == lim) update(ans, d[to][to][k + ]);
; to <= r; ++to) {
], d[i][j][k] + sum(i + , , to, ));
 == lim) update(ans, d[to][j][k + ]);
; to <= r; ++to) {
], d[i][j][k] + sum(j + , , to, ));
 == lim) update(ans, d[i][to][k + ]);
 == lim) ans = ;
 == scanf(     }
     ;
 }

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

二、图论

1、最短路

1001:

[BeiJing2006]狼抓兔子

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

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

;
;
; i <= tot; ++i)
, ;   
;
;
;
, ;
; ed = (n - ) *  * m + (m - ) *  + ;
; i <= n; ++i) {
; j <= m - ; ++j) {
 * (i - ) + j * ;
 * (i - ) + j *  - ;
 == i) {
; i <= n - ; ++i)
; j <= m; ++j) {
 * (i - ) + j *  - ;
 * (i - ) + (j - ) * ;
 == j) {
; i <= n - ; ++i)
; j <= m - ; ++j) {
             id = m *  * (i - ) + j * ;
             nid = id - ;
             addedge(id, nid, len);
             addedge(nid, id, len);
         }
 }
           printf( }
            == scanf(          == n &&  == m) puts(          == n) {
             mx = INF;
             ; i <= m - ; ++i) {
                 scanf(                 mx = min(u, mx);
             }
             printf(         }  == m) {
             mx = INF;
             ; i <= n - ; ++i) {
                 scanf(                 mx = min(mx, u);
             }
             printf(         }
         input();
         solve();
     }
     ;
 }

羞涩的代码君

1050:

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

 + ;
 + ;
 + ;
 + ];
, ; i <= ; ++i) {
; j += i)
, INF);
, ;
);
; i < prime.size(); ++i) {
 == re.first % prime[i] &&  == re.second % prime[i]) {
, ;
     ;
          ; i < m; ++i) {
         scanf(         addedge(u, v, len);
         addedge(v, u, len);
         x[idy ++] = len;
     }
  
     std::sort(x, x + idy);
     idy = std::unique(x, x + idy) - x;
  
          scanf(  
     pii ans(INF, );
     ; i < idy; ++i)
             ans = Min(ans, Cal(x[i], s, t));
  
              puts(     }          )
             printf(                      printf(     }
 }
      prepare();
      == scanf(         work();
     }
     ;
 }

2、强连通分量

1051:

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

;
;
) {
];
, , ;
; i < n; ++i) {
, ;
; i < m; ++i) {
 == sol.scc_cnt) {
, ; i < n; ++i)
, idx;
; i <= sol.scc_cnt; ++i) {
         ) {
             cnt = ;
             ; i < n; ++i)
                              printf(         }      }
 }
       == scanf(         input();
         solve();
     }
     ;
 }

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

三、利用单调性维护

1012:

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

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

 #include <iostream>
 #include <cstdio>
 #include <algorithm>
 #include <cstring>
  
 ;
  ];
      , r = c - , mid;
              mid = (l + r) >> ;
         ) {
             l = mid + ;
         }              r = mid;
         }
     }
      }
            == scanf(         c = cc = t = ;
         ; i <= m; ++i) {
             scanf(                              u = (u + t) % d;
                 ar[++cc] = u;
                  && ar[sta[c - ]] < u) --c;
                 sta[c ++] = cc;
             }                  t = bSearch(u, cc);
                 printf(             }
         }
     }
     ;
 }

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]);

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

 + ;
 == z) {
 == z) {
;
, , ; i <= r; ++i) {
; j < s; ++j) {
], Max.front().v);
], Min.front().v);
, , ; j + s -  <= c; ++j) {
; i < s; ++i) {
                 Min.pop_back();
             }
             Min.push_back(node(i, j, rmin[i][j]));
         }
  
                                       Max.pop_back();
             }
             Max.push_back(node(i, j, rmax[i][j]));
                              Min.pop_back();
             }
             Min.push_back(node(i, j, rmin[i][j]));
                              Max.pop_front();
                              Min.pop_front();
             update_max(qmax[i - s + ][j], Max.front().v);
             update_min(qmin[i - s + ][j], Min.front().v);
         }
     }
 }
      ; i <= r; ++i)
         ; j <= c; ++j)
             scanf(  
     prepare();
  
     ; i + s -  <= r; ++i) {
         ; j + s -  <= c; ++j) {
           }
     }
     printf( }
  
       == scanf(         work();
     }
     ;
 }

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

四、贪心

1029:

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

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

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

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

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

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

 #include <cstdio>
 #include <queue>
 #include <algorithm>
  
 typedef std::pair<  + ;
 pii ar[N];
  
 std::priority_queue<pii>Q;
   
       }
        
     ; i < n; ++i)
             scanf(  
     std::sort(ar, ar + n, cmp);
  
     ;
     ; i < n; ++i) {
                      Q.push(ar[i]);
             now += ar[i].first;
         }              now = now + ar[i].first - Q.top().first;
             Q.pop();
             Q.push(ar[i]);
         }
     }
  
     printf( }
       == scanf(         work();
     }
     ;
 }

1034:

经典的田忌赛马。

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

 #include <cstdio>
 #include <algorithm>
  
  + ;
   
   
      ;
          al = bl = ;
     ar = br = n - ;
                           --ar; --br;
             re += ;
         }              ++al; ++bl;
             re += ;
         }              ++al; --br;
             re += ;
         }              ++al; --br;
         }
     }
      }
      ; i < n; ++i)
         scanf(     ; j < n; ++j)
         scanf(  
     std::sort(a, a + n);
     std::sort(b, b + n);
  
     printf( * n - solve(b, a));
 }
       == scanf(         work();
     }
     ;
 }

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

五、数据结构

1、并查集

1015:

倒着做并查集

 #include <cstdio>
 #include <iostream>
 #include <cstring>
 #include <vector>
 #include <algorithm>
  ;
 ;
       };
   Edge eg[N];
   
            }
                --cnt; fa[xx] = yy;
 }
      eg[idx].u = u, eg[idx].v = v, eg[idx].nex = g[u];
     g[u] = idx++;
 }
      ; i < n; ++i) 
         fa[i]  = i;
     cnt = n; idx = ;
     memset(can, ,      memset(g, -,           ; i < m; ++i) {
         scanf(         addedge(u, v);
     }
     scanf(      ; i < c; ++i) {
         scanf(         can[ar[i]] =      }
 }
      ; i < n; ++i) {
                                                     }
     }
     ;
     ans[cc++] = cnt - c;
     ; i >= ; --i) {
         can[ar[i]] =                                             }
         ans[cc++] = cnt - i;
     }
     ; i >= ; --i)
         printf( }
       == scanf(         input();
         solve();
     }
     ;
 }

六、数学

1、计数问题

1005:

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

     
               BigInteger re = BigInteger.ONE;
                      re = re.multiply(BigInteger.valueOf(i));
              }
              Scanner cin =                                                  k = cin.nextInt();
                                           sum += k - 1;
                 ar[c++] = k - 1;
             }
         }
         BigInteger ans = Cal(sum);
                      ans = ans.divide(Cal(ar[i]));
         ans = ans.multiply(BigInteger.valueOf(n - c).pow(n - 2 - sum));
         ans = ans.multiply(Cal(n - 2)).divide(Cal(sum)).divide(Cal(n - 2 - sum));
         System.out.println(ans);
     }
  
 }