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