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;}