11.3 校内模拟赛解题报告

T1

算出 (x, y) 分别是 (2) 的几次方也就是在多少层,让 (x, y) 跳到同一层,当 (x, y) 不相等是就向上跳,记录跳的次数。

T2

概率期望 DP。
首先要发现一个性质,每个位置的颜色于这个位置修改了多少次有关,而与中间的过程没有关系。
考虑每个位置被选中了多少次?
假设一次作画,选择的区间为 ([l, r]), 区间的长度为 (len = r - l + 1), 考虑一个位置被选中其他的位置随便选的方案数,它会被选中 (2 ^ {len - 1})次, 子集的情况是 (2 ^ {len}),每个位置被选中的概率为 (frac 12)

(f[k][i][j]) 表示操作了 (k) 次,第 (i) 张画纸改成颜色 (j) 的概率。不改转移到 (f[k+1][i][j]),改转移到 (f[k+1][i][j imes h \% c]。)
最后的答案=(sum f[b[i]][i][j] imes j)(b[i]) 表示 (i) 位置被操作了多少次。
时间复杂度 (O(n ^ 4))

/*
Date:
Source:
Knowledge:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#define orz cout << "AK IOI" << "
"
#define int long long 

using namespace std;
const int maxn = 110;

int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int Max(int a, int b){
	return a > b ? a : b;
}
int Min(int a, int b){
	return a < b ? a : b;
}
int n, c, k, b[maxn];
double ans, f[maxn][maxn][maxn];
signed main()
{
	//freopen("paint.in", "r", stdin);
	//freopen("paint.out", "w", stdout);
	n = read(), c = read(), k = read();
	for(int i = 1; i <= k; i++)
	{
		int l = read(), r = read();
		b[l]++, b[r + 1]--;
	}
	for(int i = 1; i <= n; i++) b[i] += b[i - 1];
	for(int i = 1; i <= n; i++) f[0][i][1] = 1;
	for(int i = 1; i <= n; i++) //第 i 张 
	{
		for(int j = 0; j < b[i]; j++) // 改动了多少次 
		{
			for(int k = 0; k < c; k++)  // 改成什么颜色  
			{
				f[j + 1][i][k] += f[j][i][k] / 2; // 不改 
				for(int l = 0; l < c; l++) //改 
				f[j + 1][i][(k * l) % c] += f[j][i][k] / c / 2;  
			}
		} 
	} 
	for(int i = 1; i <= n; i++) 
		for(int j = 0; j < c; j++) 
			ans += f[b[i]][i][j] * j;
	printf("%.3lf", ans);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

优化:
相同颜色的纸改的结果的概率相同,而且开始都是1。
所以 (i) 那一维可以压去,即 (f[k][j]) 表示一张纸操作了 (k) 次,变成颜色 (j) 的概率。
时间复杂度 (O(n ^3))

/*
Date:
Source:
Knowledge:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#define orz cout << "AK IOI" << "
"
#define int long long 

using namespace std;
const int maxn = 110;

int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int Max(int a, int b){
	return a > b ? a : b;
}
int Min(int a, int b){
	return a < b ? a : b;
}
int n, c, k, b[maxn];
double ans, f[maxn][maxn];
signed main()
{
	//freopen("paint.in", "r", stdin);
	//freopen("paint.out", "w", stdout);
	n = read(), c = read(), k = read();
	for(int i = 1; i <= k; i++)
	{
		int l = read(), r = read();
		b[l]++, b[r + 1]--;
	}
	for(int i = 1; i <= n; i++) b[i] += b[i - 1];
	f[0][1] = 1;
	for(int j = 0; j < k ; j++) // 改动了多少次 
	{
		for(int k = 0; k < c; k++)  // 改成什么颜色  
		{
			f[j + 1][k] += f[j][k] / 2; // 不改 
			for(int l = 0; l < c; l++) //改 
			f[j + 1][(k * l) % c] += f[j][k] / c / 2;  
		}
	} 
	for(int i = 1; i <= n; i++) 
		for(int j = 0; j < c; j++) ans += f[b[i]][j] * j;
	printf("%.3lf", ans);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

T3

30 pts

100

看到数据范围都很小,可以想到状压,又因为题目中要求的是最短距离,所以就想到状压最短路。

(f[i][S]) 表示从家到点 (i),经过了状态为 (S) 的点集。

(g[i][S]) 表示从花店到点 (i),经过了状态为 (j) 的点集。

Floyd 预处理任意两点间的最短距离 dis。
上面这个 (f), (g) 数组可以预处理出来。只需要枚举 (S)(1) 的个数 (leq frac {n - 2}2 + 2)的状态即可。复杂度就被优化掉一个 n。

然后考虑首先选择哪 (frac {n - 2}2) 个花田收割,设枚举的收割的花田的状态为 S。

我们考虑收割的过程,把前一半的最短路和后一半的最短路合并起来。
直接进行搜索。
收割过程,$ res1 = min(res1, f[S | 1][x] + g[M ^ (S | 1)][y] + dis[x][y])(。 播种过程,)res2 = min(res2, g[S | (1 << n - 1)][x] + f[M ^ (S | (1 << n - 1))][y] + dis[x][y])$。

/*
Date:
Source:
Knowledge:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#define orz cout << "AK IOI" << "
"

using namespace std;
const int maxn = 2e6 + 10;

int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int Max(int a, int b){
	return a > b ? a : b;
}
int Min(int a, int b){
	return a < b ? a : b;
}

int n, m, N, M, ans = 0x3f3f3f3f;
int dis[22][22], f[maxn][22], g[maxn][22];
int a[22], sc = 0;
int b[22], top = 0;


void dfs(int S, int pos, int cnt) 
{
    if(cnt > N) return ;
    if(pos == n) 
	{
        if(cnt != N) return ;
        int res1 = 0x3f3f3f3f, res2 = 0x3f3f3f3f;
        for(int i = 1; i <= sc; i++) 
		{
            for(int j = 1; j <= top; j++) 
			{
                int x = a[i], y = b[j];
                res1 = min(res1, f[S | 1][x] + g[M ^ (S | 1)][y] + dis[x][y]);
                res2 = min(res2, g[S | (1 << n - 1)][x] + f[M ^ (S | (1 << n - 1))][y] + dis[x][y]);
            }
        }
        ans = min(ans, res1 + res2);
        return ;
    }
    a[++sc] = pos;
    dfs(S | (1 << pos - 1), pos + 1, cnt + 1);
    --sc;
    b[++top] = pos;
    dfs(S, pos + 1, cnt);
    --top;
}

signed main()
{
	n = read(), m = read();
	N = (n - 2) / 2, M = (1 << n) - 1;
	memset(dis, 0x3f, sizeof dis);
	for(int i = 1; i <= n; i++) dis[i][i] = 0;
	for(int i = 1, u, v, w; i <= m; i++) 
	{
	    u = read() + 1, v = read() + 1, w = read();
	    dis[u][v] = dis[v][u] = min(dis[u][v], w);
    }
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++) 
            for(int j = 1; j <= n; j++) 
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
    if(n == 3) 
	{
        printf("%lld
", 1ll * (dis[1][2] + dis[2][3]) * 2);
        return 0; 
    }
    memset(f, 63, sizeof f);
    f[1][1] = 0;
    for(int S = 0; S < (1 << n); S++) 
	{
        int cnt = 0;
        for(int i = 1; i <= n; i++) if(S & (1 << i - 1)) cnt++;
        if(cnt > N + 2) continue;
        for(int i = 1; i <= n; i++) 
		{
            if(!(S & (1 << i - 1))) continue;
            for(int j = 1; j <= n; j++) 
			{
                if(S & (1 << j - 1)) continue;
                f[S | (1 << j - 1)][j] = min(f[S | (1 << j - 1)][j], f[S][i] + dis[i][j]);
            }
        }
    }
    memset(g, 63, sizeof g);
    g[(1 << n - 1)][n] = 0;
    for(int S = 0; S < (1 << n); S++) 
	{
        int cnt = 0;
        for(int i = 1; i <= n; i++) if(S & (1 << i - 1)) cnt++;
        if(cnt > N + 2) continue;
        for(int i = 1; i <= n; i++) 
		{
            if(!(S & (1 << i - 1))) continue;
            for(int j = 1; j <= n; j++) 
			{
                if(S & (1 << j - 1)) continue;
                g[S | (1 << j - 1)][j] = min(g[S | (1 << j - 1)][j], g[S][i] + dis[i][j]);
            }
        }
    }
    dfs(0, 2, 0);
    printf("%d", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/yangchengcheng/p/15506536.html