Task 14

来源:互联网 发布:chart.js 自定义 提示 编辑:程序博客网 时间:2024/06/05 05:33

T1

题目大意:

一棵特别大的二叉树上求LCA

简单分析:

求就求白, 没啥法

标算:

根据二叉树结点编号的性质

#include<cstdio>#include<cmath>#include<algorithm>using namespace std;typedef long long ll;ll x, y; int n;int dep(long long x) {return (int)(log(x)/log(2)); }int main() {    scanf("%d", &n);    for(int i = 1; i <= n; ++i) {        scanf("%lld%lld", &x, &y);        int ans = 0;        if(x == y) { printf("0\n"); continue; }        // x > y        if(dep(x) < dep(y)) swap(x, y);        while(dep(x) > dep(y)) { x /= 2; ans++; }        if(x == y) { printf("%d\n", ans); continue; }        while(x != y) { x /= 2; y /= 2; ans += 2; }        printf("%d\n", ans);    }    return 0;}

T2

题目大意:

你把n张画纸铺成一排,并将它们从1到n编号。你一共有c种颜色可用,这些颜色可以用0到c-1来编号。初始时,所有画纸的颜色都为1。你一共想进行k次作画,第i次作画时,你会等概率随机地选闭区间[Li,Ri]内的画纸的一个子集(可以为空),再随机挑一种颜色bi,并把挑出来的画纸都涂上该颜色。原有颜色a的画纸在涂上颜色b后,颜色会变成(a*b) mod c,这是这个世界的规律。
求出在k次作画结束后,每张画纸上的颜色对应的数字相加之和的期望

简单分析:

看起来是在算期望, 很高端的样子, 但是实质上是算概率, 然后将值按概率加权即可
当时抱着大概可以dp解的想法就写了
推导出的结论: 从一个集合中选取任意子集, 每个元素被选到的概率都是1/2

标算:

状态: col[i][j] 表示i点是j号颜色的概率是多少
转移: 枚举下一次转到那个颜色
注意: 颜色变化方式与reality不同

#include<cstdio>#include<cstring>const int N = 110;double ans, col[N][N], tmp[N];int n, c, k;int main() {    scanf("%d%d%d", &n, &c, &k);    double sb = 1.0/c; //每种颜色的概率     for(int i = 1; i <= n; ++i) col[i][1] = 1.0; //所有点在最初都是1号颜色     for(int i = 1; i <= k; ++i) {        int l, r;        scanf("%d%d", &l, &r);        for(int j = l; j <= r; ++j) { //区间内每一个点             memset(tmp, 0, sizeof(tmp));            for(int a = 1; a < c; ++a) { //该点每一种颜色                 col[j][a] = col[j][a]*(0.5);                for(int b = 1; b < c; ++b) {                    tmp[(a*b)%c] += col[j][a]*sb;                }            }            for(int a = 1; a < c; ++a) { // 将修改与已知答案合并                 col[j][a] += tmp[a];            }         }    }    for(int i = 1; i <= n; ++i) {        for(int j = 1; j < c; ++j) {            ans += col[i][j]*(double)(j); //由概率导出期望         }    }    printf("%.3lf\n", ans);    return 0;}

T3

不可做的题目大意:

收获香子兰(香子兰: qwq), 必须从家到花田收获, 再到花店卖掉, 再到花田播种, 再回家
限制: 前(n-2)/2个被收割的花田也是前(n-2)/2个被播种的

用命分析:

没命了qwq

咋都看不懂的标算:

状态压缩DP
Floyd预处理两两之间最短路,并预处理:
F[i][sta]表示从家开始,当前走到点i,已经走过sta中的点,走过的最短距离是多少
G[i][sta]表示从花店开始,当前走到点i,已经走过sta中的点,走过的最短距离是多少
枚举先收割哪些花田,记为A,其余的花田记为B
分交货前和交货后两段,单独计算最短距离。以交货前为例:
枚举A中最后一个收割的点i、B中第一个收割的点j
求min{F[i][A]+dis(i,j)+G[j][B]}

#include<cstdio>#include<algorithm>using namespace std;const int inf = 1e9 + 7;const int N = 24;const int M = 1050000;int a[N][N], d[N][N], f[2][N][M], e[N], cnt[M];int n, m, ans;void pre() { //预处理每个二进制数中有几个1     e[0] = 1;    for(int i = 1; i <= 22; ++i) e[i] = e[i-1]<<1;    for(int i = 0; i < e[20]; ++i) {        for(int x = i; x != 0; x >>= 1) {            cnt[i] += x&1;        }    }    for(int i = 1; i <= n; ++i) {        for(int j = 1; j <= n; ++j) {            d[i][j] = inf*(i!=j);        }    }}void Floyd() {    for(int k = 1; k <= n; ++k) {        for(int i = 1; i <= n; ++i) {            for(int j = 1; j <= n; ++j) {                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);            }        }    }}int main() {    scanf("%d%d", &n, &m);    pre();    for(int i = 1; i <= m; ++i) {        int x, y, z;        scanf("%d%d%d", &x, &y, &z);        ++x; ++y;        if(z < d[x][y]) d[x][y] = d[y][x] = z;    }    Floyd();    if(n == 3) {        printf("%d\n", (d[1][2]+d[2][3])*2);        return 0;    }    int n1 = (n-2)/2;    int n2 = n-2 - n1;//  求从家、花店开始, 走到点i, 经过的点为j的最短路//  q = 0: 从家开始;  q = 1: 从花店开始     for(int q = 0; q <= 1; ++q) {        //初始状态         for(int i = 1; i <= n; ++i) {            for(int j = 0; j < e[n-2]; ++j) {                f[q][i][j] = inf;            }        }        if(q == 0) {            for(int i = 2; i < n; ++i) {                f[q][i][e[i-2]] = d[1][i];            }        } else if(q == 1) {            for(int i = 2; i < n; ++i) {                f[q][i][e[i-2]] = d[n][i];            }        }        //dp         for(int j = 1; j < e[n-2]; ++j) {            if(cnt[j] < n2) {                for(int i = 2; i < n; ++i) {                    if(f[q][i][j] < inf) {                        for(int k = 2; k < n; ++k) {                            f[q][k][j|e[k-2]] = min(f[q][k][j|e[k-2]],                                 f[q][i][j]+d[i][k]);                        }                    }                }            }        }    }    ans = inf;    //枚举先走到的一半为sta     for(int sta = 0; sta < e[n-2]; ++sta) {        if(cnt[sta] == n1) {            //前半段             int x = inf; //x记录前半段的最短距离            //枚举前一半中最后一个收割的点是i             for(int i = 2; i < n; ++i) {                if(sta&e[i-2]) {                    //枚举后一半中第一个收割的点是j                     for(int j = 2; j < n; ++j) {                        if(!(sta&e[j-2])) {                            x = min(x,                                 f[0][i][sta]+d[i][j]+f[1][j][e[n-2]-1-sta]);                        }                    }                }            }            //后半段            //枚举前一半中最后一个播种的点是i             for(int i = 2; i < n; ++i) {                if(sta&e[i-2]) {                    //枚举后一半中第一个播种的点是j                     for(int j = 2; j < n; ++j) {                        if(!(sta&e[j-2])) {                            ans = min(ans,                                 x+f[1][i][sta]+d[i][j]+f[0][j][e[n-2]-1-sta]);                        }                    }                }            }        }    }    printf("%d\n", ans);    return 0;}