• Codeforces Round #148 (Div. 1)



    A


    wool sequence 表示一个序列中能够找到一个连续的子区间使得区间异或值为0

    那么求的是不含这样的情况的序列个数

    题目中数据范围是。在0~2^m - 1中选n个数作为一个序列 

    n和m都是10^5


    细致思考一下。
    第一位 有2^m-1种情况
    第二位因为不能跟其一样  有2^m-2种情况
    第三位因为不能跟第二位一样。而且不能跟前两位的异或值一样。有2^m-3种情况
    依次类推,得到公式
    (2^m-1)*(2^m-2)*...*(2^m-n)
    int mod = 1000000009;
    int main()
    {
        int n, m;
        scanf("%d%d", &n, &m);
        if(m < 20 && (1 << m) <= n ) {
            printf("0
    ");
            return 0;
        }
        long long ans = 1;
        for(int i = 0; i < m; i++) {
            ans = ans * 2LL % mod;
        }
        long long res = 1;
        for(int i = 1; i <= n; i++) {
            long long tmp = ans - i;
            if(tmp < 0) tmp += mod;
            res = res * tmp % mod;
        }
        printf("%I64d
    ", res);
        return 0;
    }



    B

    给出一个序列a和一个数值h

    将序列划分为两部分,某部分能够为空

    然后f函数式这么计算

    假设两个数在同个部分

    f函数的值就是两个数的和

    不在同部分就两个数的和加上h

    求一种划分使得f函数的最大值和最小值的差值最小


    有一种贪心方案, 

    就是f的最大值一定跟序列中最大的几个有关

    最小值一定跟序列中最小的几个有关

    这里我取得最小的5个和最大的5个

    直接枚举放第一部分或者第二部分就可以


    为啥这样能够

    细致想一下。

    能够发现其它的不论什么情形都没有这样的情况下优


    int n, h;
    struct node {
        int x, id;
    }p[111111], tmp[15];
    int ans[111111];
    bool cmp(node x, node y) {
        return x.x < y.x;
    }
    vector<node>lft, rht;
    int main()
    {
        scanf("%d%d", &n, &h);
        for(int i = 0; i < n; i++) {
            scanf("%d", &p[i].x);
            p[i].id = i;
        }
        sort(p, p + n, cmp);
     
        int cnt = 0;
        if(n > 10) {
            for(int i = 0; i < 5; i++) tmp[cnt++] = p[i];
            for(int i = n - 5; i < n; i++) tmp[cnt++] = p[i];
        } else {
            cnt = n;
            for(int i = 0; i < n; i++) tmp[i] = p[i];
        }
        int vs[12];
        int d = INF;
        int num = 0;
        for(int i = 0; i < (1 << cnt); i++) {
            lft.clear();
            rht.clear();
            for(int j = 0; j < cnt; j++) {
                node x = tmp[j];
                if((1 << j) & i) {
                    lft.push_back(x);
                } else rht.push_back(x);
            }
            int mx = 0;
            int mi = INF;
            int sz1 = lft.size();
            for(int j = 0; j < sz1; j++) {
                for(int k = j + 1; k < sz1; k++) {
                    mx = max(mx, lft[j].x + lft[k].x);
                    mi = min(mi, lft[j].x + lft[k].x);
                }
            }
            int sz2 = rht.size();
            for(int j = 0; j < sz2; j++) {
                for(int k = j + 1; k < sz2; k++) {
                    mx = max(mx, rht[j].x + rht[k].x);
                    mi = min(mi, rht[j].x + rht[k].x);
                }
            }
            for(int j = 0; j < sz1; j++) {
                for(int k = 0; k < sz2; k++) {
                    mx = max(mx, lft[j].x + rht[k].x + h);
                    mi = min(mi, lft[j].x + rht[k].x + h);
                }
            }
            if(d > mx - mi) {
                d = mx - mi;
                num = 0;
                for(int j = 0; j < sz1; j++) vs[num++] = lft[j].id;
            }
        }
        printf("%d
    ", d);
        for(int i = 0; i < n; i++) ans[i] = 2;
        for(int i = 0; i < num; i++) ans[vs[i]] = 1;
        for(int i = 0; i < n; i++) printf("%d ", ans[i]);
        printf("
    ");
        return 0;
    }


    C

    一颗无根树

    有向边

    从树上不多于两个点出发,遍历全部的点。

    问须要改变最少多少个有向边的方向

    那么就是一个树dp了

    枚举根作为第一个点。

    然后第二个点就须要dp

    从根往下遍历全部的点的话,须要改动边的方向非常easy算出来。每一个点訪问自己全部子树须要改动的边数也非常easy算

    然后数组开 dp[n][2]

    dp[u][0]代表从u走到根须要改动多少边

    dp[u][1]表示从跟走到u须要改动多少边


    struct node {
        int v, w;
        node() {}
        node(int _v, int _w) {v = _v; w = _w;}
    };
    vector<node> g[3333];
    int ss[3333], dp[3333][2];
    int total;
    void dfs0(int u, int f, int x) {
        total += x;
        ss[u] = ss[f] + x;
        int sz = g[u].size();
        for(int i = 0; i < sz; i++) {
            int v = g[u][i].v;
            int w = g[u][i].w;
            if(v == f) continue;
            dfs0(v, u, w);
        }
    }
    void dfs(int u, int f, int x) {
        if(f == 0) {
            dp[u][0] = dp[u][1] = 0;
        } else {
            dp[u][1] = dp[f][1] + x;
            dp[u][0] = min(dp[f][0], dp[f][1]) + !x;
        }
        int sz = g[u].size();
        for(int i = 0; i < sz; i++) {
            int v = g[u][i].v;
            int w = g[u][i].w;
            if(v == f) continue;
            dfs(v, u, w);
        }
    
    }
    int n;
    int main()
    {
        int u, v;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) g[i].clear();
        for(int i = 1; i < n; i++) {
            scanf("%d%d", &u, &v);
            g[u].push_back(node(v, 0));
            g[v].push_back(node(u, 1));
        }
        int ans = INF;
        for(int i = 1; i <= n; i++) {
            ss[i] = 0;
            total = 0;
            dfs0(i, 0, 0);
            dfs(i, 0, 0);
            for(int j = 1; j <= n; j++) ans = min(ans, total - ss[j] + min(dp[j][0], dp[j][1]));
        }
        printf("%d
    ", ans);
        return 0;
    }



    D.

    留坑  不会


    E

    看了ACMonster的才大约懂做。事实上还是非常模糊


    有个有向图,边的长度都一样

    n<=100

    有个人要从起点到终点去,必须做公交

    然后有若干辆公交  有各自的起点和终点

    保证每秒都有一辆公交发出。意思就是那个人一到某个点就能够换乘对应的公交

    然后这些公交非常奇怪。 从自己的起点到终点,它会随机走一条最短路径过去

    如今问,最小的(他在最坏情况下的换乘次数)

    就是寻找一条线路使得他在最坏情况下换乘bus的次数最小


    这个因为是求换乘次数,所以这个图就没法按dag那种dp方法

    首先预处理随意两点间的最短路

    然后把每一个点必定经过的公交线路存下来

    然后就倒着dp

    从终点往起点dp

    很之暴力

    令dp[i][j]表示人在点i在j车上时最小的换乘次数

    可是有两种情况

    一种是这个点一定在这个公交线路上

    一种是这个点可能会在这个公交线路上或者就不在这个公交线路上

    第一种情况是终于的合法解

    另外一种用来辅助第一种


    然后更新的时候。对于每一个点,枚举公交车,然后看这个点的邻接点,相对于这个路径是不是合法的。意思就是邻接点离该路径的终点应该是更近一步的,

    即使这个点不会出如今这个公交路径上。也要更新,这是为了让那些后面的关键的点能够更新到,事实上就相当于虚拟了一下,将这个公交的线路给延伸了

    对于这些合法的邻接点。 选择一条最糟糕的

    然后枚举换乘的时候。就必须是这个点一定在这个公交线路上,  由于换乘一定是在这些点上才干换乘,不然最糟糕的情况是等不到车的

    然后假设有最优值被更新,那么整个dp再反复这个过程 


    int dis[111][111], dp[111][111];
    vector<int>g[111], bus[111];
    int bx[111], by[111], m, n, t, src, des;
    bool pass[111][111];
    int main()
    {
        int u, v;
        scanf("%d%d%d%d", &n, &m, &src, &des);
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                if(i != j) dis[i][j] = INF;
        for(int i = 0; i < m; i++) {
            scanf("%d%d", &u, &v);
            dis[u][v] = 1;
            g[u].push_back(v);
        }
        for(int k = 1; k <= n; k++) {
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= n; j++)
                    if(dis[i][j] > dis[i][k] + dis[k][j])
                        dis[i][j] = dis[i][k] + dis[k][j];
            }
        }
        m = 0;
        scanf("%d", &t);
        while(t--) {
            scanf("%d%d", &u, &v);
            if(dis[u][v] != INF) {
                m++;
                bx[m] = u;
                by[m] = v;
            }
        }
        for(int i = 1; i <= m; i++) {
            u = bx[i], v = by[i];
            for(int j = 1; j <= n; j++) {
                if(dis[u][v] == dis[u][j] + dis[j][v]) pass[i][j] = 1;
                for(int k = 1; k <= n; k++) {
                    if(k != j && dis[u][k] == dis[u][j] && dis[k][v] == dis[j][v]) {
                        pass[i][j] = 0;
                        break;
                    }
                }
                if(pass[i][j]) bus[j].push_back(i);
            }
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                if(i == des && pass[j][i]) dp[i][j] = 0;
                else dp[i][j] = INF;
            }
        }
        bool flag = 1;
        while(flag) {
            flag = 0;
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= m; j++) {
                    int mx = -1;
                    for(int k = 0; k < g[i].size(); k++) {
                        v = g[i][k];
                        if(dis[i][by[j]] != dis[v][by[j]] + 1) continue;
                        //if(!pass[j][i] || !pass[j][v]) continue;
                        mx = max(mx, dp[v][j]);
                    }
                    if(mx != -1 && mx < dp[i][j]) {
                        dp[i][j] = mx;
                        flag = 1;
                    }
                    for(int k = 0; k < bus[i].size(); k++) {
                        v = bus[i][k];
                        if(dp[i][v] + 1 < dp[i][j]) {
                            dp[i][j] = dp[i][v] + 1;
                            flag = 1;
                        }
                    }
                }
            }
        }
        int ans = INF;
        for(int i = 1; i <= m; i++)
            if(pass[i][src]) ans = min(ans, dp[src][i] + 1);
        if(ans == INF) ans = -1;
        printf("%d
    ", ans);
        return 0;
    }




  • 相关阅读:
    21-MySQL-Ubuntu-快速回到SQL语句的行首和行末
    2- SQL语句的强化
    1-数据准备
    20-MySQL-Ubuntu-数据表的查询-子查询(九)
    19-MySQL-Ubuntu-数据表的查询-自关联(八)
    18-MySQL-Ubuntu-数据表的查询-连接(七)
    17-MySQL-Ubuntu-数据表的查询-分页(六)
    16-MySQL-Ubuntu-数据表的查询-分组与聚合(五)
    15-MySQL-Ubuntu-数据表的查询-聚合函数(四)
    14-MySQL-Ubuntu-数据表的查询-范围查询(三)
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/6795174.html
Copyright © 2020-2023  润新知