zoukankan      html  css  js  c++  java
  • HDU 多校联合第五场

    01

    树形dp。。。比赛完看了看解题报告。发现我的状态写的太搓了,想到了先只考虑半数的,然后在一个全是A(或B)的块里面补上一个全的。当时没想着把这个也加到状态里去,写特判写的想哭。。。!

    f[t][f][0] 表示t为根的子树,t被f攻击,并且算是f攻击的“连通块“里没有一个节点的攻击值是全值的最小值。

    f[t][f][1] 表示t为根的子树,t被f攻击,并且算是f攻击的“连通块“里有一个节点的攻击值是全值的最小值。

    sum = Σ{min(f[t->i][f][0], f[t->i][f^1][1])};
    plus = min{ f[t->i][f][1] - min(f[t->i][f][0], f[t->i][f^1][1])}  //把某个值补全
    
    f[t][f][0] = cost[t][f]/2 + sum;
    f[t][f][1] = min(cost[t][f] + sum, cost[t][f]/2 + sum + plus);
    View Code
    using namespace std;
    
    const int N = 220;
    
    int dp[N][2][2];
    int c[N][2];
    int mp[N][N], n;
    bool vis[N];
    
    void dfs(int t, int f) {
        if(dp[t][f][0] != -1 && dp[t][f][1] != -1)    return ;
    
        int sum = 0, p = inf, i;
        for(i = 1; i <= n; ++i) {
            if(!vis[i] && mp[t][i]) {
                vis[i] = true;
                dfs(i, f); dfs(i, f^1);
                sum += min(dp[i][f][0], dp[i][f^1][1]);
                p = min(dp[i][f][1] - min(dp[i][f][0], dp[i][f^1][1]), p);
    
                ///det = min{dp[v][j][1] - min(dp[v][j][0], dp[v][1-j][1])};
    
                vis[i] = false;
            }
        }
        dp[t][f][0] = sum + c[t][f]/2;
        dp[t][f][1] = min(c[t][f] + sum, c[t][f]/2 + sum + p);
    }
    
    void init(int n) {
        for(int i = 0; i<= n; ++i) {
            for(int j = 0; j < 2; ++j) {
                for(int k = 0; k < 2; ++k)  dp[i][j][k] = -1;
            }
        }
        CL(vis, false);
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int i, x, y, ans;
        while(~scanf("%d", &n)) {
            init(n);
            for(i = 1; i <= n; ++i) scanf("%d", &c[i][0]);
            for(i = 1; i <= n; ++i) scanf("%d", &c[i][1]);
            CL(mp, 0);
            for(i = 1; i < n; ++i) {
                scanf("%d%d", &x, &y);
                mp[x][y] = mp[y][x] = 1;
            }
            init(n); vis[1] = true;
            dfs(1, 0);
            ans = dp[1][0][1];
            //printf("%d ", ans);
            init(n); vis[1] = true;
            dfs(1, 1);
            //printf("%d\n", dp[1][1][1]);
            ans = min(ans, dp[1][1][1]);
            printf("%d\n", ans);
        }
        return 0;
    }

    03 01背包问题的变形

    按斜率排序,因为01背包的过程是由上一层的状态推当前层的状态。所以可以把斜率相同的点放到一个背包里,按离原点的距离进行压缩,把前面所有点的状态压到当前点,然后决策这个点选还是不选。因为这些点放到同一层里进行决策,所以不会出现重复选择某个点的情况。

    View Code
    using namespace std;
    
    const int N = 220;
    
    int f[40010];
    
    struct node {
        int x;
        int y;
        int t;
        int val;
    
        bool operator < (const node tmp) const {
            if(y*tmp.x == x*tmp.y)  return y < tmp.y;
            return y*tmp.x < x*tmp.y;
        }
    }p[N];
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int n, T, i, j, k;
        int t, v, l, cas = 0;
        while(~scanf("%d%d", &n, &T)) {
            for(i = 0; i < n; ++i) {
                scanf("%d%d%d%d", &p[i].x, &p[i].y, &p[i].t, &p[i].val);
            }
            sort(p, p + n);
            //for(i = 0; i < n; ++i)  printf("%d %d %d %d\n", p[i].x, p[i].y, p[i].t, p[i].val);
    
            CL(f, 0);
            for(i = 0; i < n; ++i) {
                k = i;
                while(p[i].y*p[k+1].x == p[k+1].y*p[i].x && k + 1 < n)   k++;
    
                for(j = T; j >= p[i].t; --j) {
                    t = 0, v = 0;
                    for(l = i; l <= k; ++l) {
                        t += p[l].t; v += p[l].val;    //压缩
                        if(j - t >= 0)   {
                            if(f[j-t] != -inf) {
                                f[j] = max(f[j], f[j-t] + v);
                            }
    
                        } else  break;
                    }
                }
                i = k;
            }
            int ans = 0;
            for(i = 0; i <= T; ++i) ans = max(ans, f[i]);
    
            printf("Case %d: %d\n", ++cas, ans);
        }
        return 0;
    }

    04 完全是推规律

    这个题M是我推出来的, 是gb推出来的。Orz

    View Code
    int main() {
        //freopen("data.in", "r", stdin);
    
        int t, n, m, x;
        LL k, i;
        scanf("%d", &t);
        while(t--) {
            scanf("%d", &n);
            if(n == 1)  {
                printf("2 2\n");
            } else if(n == 2) {
                printf("3 3\n");
            } else {
                m = 2; x = 2; i = 1; k = 1;
                while(1) {
                    if(n - x <= 0)   break;
                    n -= x; m += 1 + x;
                    k += i*LL(x) + i + 1; x += 2;
                    i++;
                }
                m += n - 1;
                k += i*LL(n);
                cout << m << " " << k << endl;
            }
        }
        return 0;
    }

    06

    详见:Miller-Rabin + poLLard-rho 模板

    07 

    首先明白一点。每种方案的循环次数 = 各循环节大小的最小共倍数。

    然后就可以枚举循环节的大小,要求这些循环节的和<= N,为什么可以<,因为其余的可以全部是1;

    因为是要找最小共倍数的个数,所以如果这些数包含大于一个质因子,那么最小共倍数就是一定的,多加几个只会增加和,不会得到新的最小共倍数。

    所以可以推出:枚举的所有循环节大小都是只包含一个质因子。然后递推或者记忆化搜索就可以了。

    f[i][n]表示最大为n,当前枚举的素数是素数表中第i个素数

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <ctime>
    #include <queue>
    #include <map>
    #include <sstream>
    
    #define CL(arr, val)    memset(arr, (val), sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   ((l) + (r)) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)    (1 << (x))
    #define iabs(x)  ((x) > 0 ? (x) : -(x))
    
    typedef long long LL;
    const double eps = 1e-8;
    //const int inf = ~0u>>2;
    
    using namespace std;
    
    const int N = 1024;
    
    LL f[N][N];
    int prime[N], cnt, lim;
    bool isp[N];
    
    void init() {
        CL(isp, true);
        CL(prime, 0);
        int i, j;
        for(i = 2; i < N; ++i) {
            for(j = i*i; j < N; j += i) isp[j] = false;
        }
        cnt = 0;
        for(i = 2; i < N; ++i) {
            if(isp[i])  {prime[cnt++] = i; }
        }
    }
    
    LL solve(int i, int n) {
        if(prime[i] > n)    return 1;
    
        if(f[i][n] != -1)   return f[i][n];
    
        f[i][n] = solve(i + 1, n);
        LL x = prime[i];
        while(x <= n) {
            f[i][n] += solve(i + 1, n - x);
            x *= prime[i];
        }
        return f[i][n];
    }
    
    int main() {
        freopen("data.in", "r", stdin);
    
        init();
        int n;
        while(~scanf("%d", &n)) {
            CL(f, 0xff);
            cout << solve(0, n) << endl;
        }
        return 0;
    }

    11

    Lucas定理:

    给出n, m, p。

    把n, m写成p进制:

    这里是二进制:

    比如 n=1001101,m是从000000到1001101的枚举,我们知道在该定理中 C(0,1)=0, C(0, 0) = 0!/(0!*(0-0)!) = 1.

    因此如果n=1001101的0对应位置的m二进制位为1那么C(n,m) % 2==0, 因此m对应n为0的位置只能填0,而1的位置填0,填1都是1(C(1,0)=C(1,1)=1);

    不影响结果为奇数,并且保证不会出n的范围,因此所有的情况即是n中1位置对应m位置0,1的枚举,那么结果很明显就是:2^(n中1的个数)

     

  • 相关阅读:
    1133catalan数二维变种
    HDU2254 奥运 矩阵应用
    hdu1134大数+catalan数
    C#编写一个控制台应用程序,输入三角形或者长方形边长,计算其周长和面积并输出。
    简单工厂
    每日总结
    C#编写一个控制台应用程序,可根据输入的月份判断所在季节
    使用Java api对HBase 2.4.5进行增删改查
    C#编写程序,用 while 循环语句实现下列功能:有一篮鸡蛋,不止一个,有人两个两 个数,多余一个,三个三个数,多余一个,再四个四个地数,也多余一个,请问这篮鸡蛋至 少有多少个
    每日总结
  • 原文地址:https://www.cnblogs.com/vongang/p/2629111.html
Copyright © 2011-2022 走看看