zoukankan      html  css  js  c++  java
  • [bzojlydsy大视野在线测评]题解(持续更新)

    目录:

    一、DP

    二、图论

      1、最短路

      2、强连通分量

    三、利用单调性维护

    四、贪心

    五、数据结构

      1、并查集

    六、数学

      1、计数问题

      2、数学分析 

    七、博弈

    八、搜索

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

    一、DP:


    1003: 

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

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


     1009:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1021:

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

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

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

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

    1025:

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

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

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

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

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

    我们可以DP解决

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

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

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

    1026:

    [SCOI2009]windy数

     数位dp:

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

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

     1037:

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

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

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

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

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

    1044:

    [HAOI2008]木棍分割

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

    第二问dp做:

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

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

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

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

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

    1046:

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

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

    1048:

    记忆化搜索 

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

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

    1049:

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

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

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

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

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

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


    1072:

    [SCOI2007]排列perm

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

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

    比较好的方法是状压DP,

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

    我们有转移方程

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

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

    1084:

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

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

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

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

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

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


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

    二、图论

    1、最短路


    1001:

    [BeiJing2006]狼抓兔子

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

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

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

     1050:

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

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

    2、强连通分量


    1051:

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

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


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

    三、利用单调性维护


    1012:

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

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

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


    1047:

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

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

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

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

    复杂度是O(A*B)

    然后

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

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

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

      1 /**************************************************************
      2     Problem: 1047
      3     User: pikapika
      4     Language: C++
      5     Result: Accepted
      6     Time:2356 ms
      7     Memory:20764 kb
      8 ****************************************************************/
      9  
     10 #include <cstdio>
     11 #include <algorithm>
     12 #include <cstring>
     13 #include <queue>
     14  
     15 struct node {
     16     int x, y, v;
     17     node(int _x, int _y, int _v) {
     18         x = _x, y = _y, v = _v;
     19     }
     20     node() {}
     21 };
     22 std::deque<node> Max, Min;
     23  
     24 const int N = 1000 + 10;
     25  
     26 int ar[N][N];
     27 int r, c, s;
     28  
     29 int ans;
     30 int rmax[N][N], rmin[N][N];
     31 int qmax[N][N], qmin[N][N];
     32  
     33 void update_max(int& z, int v) {
     34     if (-1 == z) {
     35         z = v;
     36     } else {
     37         z = std::max(z, v);
     38     }
     39 }
     40 void update_min(int& z, int v) {
     41     if (-1 == z) {
     42         z = v;
     43     } else {
     44         z = std::min(z, v);
     45     }
     46 }
     47 void prepare() {
     48     ans = (int)(1e9) + 10;
     49     //////////////////////////////////
     50     memset(rmax, -1sizeof(rmax));
     51     memset(rmin, -1sizeof(rmin));
     52     for (int i = 1; i <= r; ++i) {
     53         while (!Max.empty()) Max.pop_back();
     54         while (!Min.empty()) Min.pop_back();
     55  
     56         for (int j = 1; j < s; ++j) {
     57             while (!Max.empty() && Max.back().v < ar[i][j]) {
     58                 Max.pop_back();
     59             }
     60             Max.push_back(node(i, j, ar[i][j]));
     61             while (!Min.empty() && Min.back().v > ar[i][j]) {
     62                 Min.pop_back();
     63             }
     64             Min.push_back(node(i, j, ar[i][j]));
     65         }
     66  
     67         for (int j = s; j <= c; ++j) {
     68             while (!Max.empty() && Max.back().v < ar[i][j]) {
     69                 Max.pop_back();
     70             }
     71             Max.push_back(node(i, j, ar[i][j]));
     72             while (!Min.empty() && Min.back().v > ar[i][j]) {
     73                 Min.pop_back();
     74             }
     75             Min.push_back(node(i, j, ar[i][j]));
     76             while (!Max.empty() && Max.front().y <= j - s)
     77                 Max.pop_front();
     78             while (!Min.empty() && Min.front().y <= j - s)
     79                 Min.pop_front();
     80  
     81             update_max(rmax[i][j - s + 1], Max.front().v);
     82             update_min(rmin[i][j - s + 1], Min.front().v);
     83 //            printf("%d*****%d ", 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 ", qmax[i][j], qmin[i][j]);
    133             ans = std::min(ans, qmax[i][j] - qmin[i][j]);
    134         }
    135     }
    136     printf("%d ", ans);
    137 }
    138  
    139 int main() {
    140     while (3 == scanf("%d%d%d", &r, &c, &s)) {
    141         work();
    142     }
    143     return 0;
    144 }
    View Code 


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

    四、贪心


    1029:

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

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

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

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

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

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

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


    1034:

    经典的田忌赛马。

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

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

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

    五、数据结构

    1、并查集


    1015:

    倒着做并查集

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


    六、数学

    1、计数问题


    1005:

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

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

    /*

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

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

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

    已知序列还原树的算法:

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

    */

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


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


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

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

    1008:

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

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

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

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

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


    2、数学分析

    1045:

    [HAOI2008] 糖果传递

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

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

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

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

    七、博弈

    1022:

    经典的ANTI-NIM博弈问题

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

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

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

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

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

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


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

    八、搜索


    1024:

    搜索

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

     1 /**************************************************************
     2     Problem: 1024
     3     User: pikapika
     4     Language: C++
     5     Result: Accepted
     6     Time:184 ms
     7     Memory:804 kb
     8 ****************************************************************/
     9  
    10 #include <cstdio>
    11 #include <algorithm>
    12  
    13 const double INF = 1e100;
    14  
    15 double dfs(double x, double y, int c) {
    16     if (y > x) std::swap(x, y);
    17     if (1 == c) return x / y;
    18     else {
    19         double re = INF;
    20         for (int i = 1; i < c; ++i) {
    21             re = std::min(re, std::max(dfs(x, y * i * (1. / c), i), dfs(x, y - y * i * (1. / c), c - i)));
    22         }
    23         for (int i = 1; i < c; ++i) {
    24             re = std::min(re, std::max(dfs(x * i * (1. / c), y, i), dfs(x - x * i * (1. / c), y, c - i)));
    25         }
    26         return re;
    27     }
    28 }
    29 int main() {
    30     double x, y;
    31     int c;
    32     while (3 == scanf("%lf%lf%d", &x, &y, &c)) {
    33         printf("%.6f ", dfs(x, y, c));
    34     }
    35     return 0;
    36 }
    View Code
  • 相关阅读:
    框架,样式表的一些认解,今天这节可有点不在状态,正在规划学习流程,让自己更快的提升起来。看着其他小伙伴都太厉害啦。努力!0909上
    表单元素的了解和表单元素分类,代码的认解。 0908下
    表格标题——简单表格的理解与认识 0906下
    HDU 1024 DP Max Sum Plus Plus
    zoj 1670 Jewels from Heaven
    BestCoder Round #68 (div.2) 1002 tree
    POJ 2986 A Triangle and a Circle 圆与三角形的公共面积
    609C Load Balancing
    609B Load Balancing
    codeforce 609A
  • 原文地址:https://www.cnblogs.com/hewifi/p/3307793.html
Copyright © 2011-2022 走看看