CSP-S模拟XX && 2022赛前模测 提高组验题-14 T3

C 修水管

由于是我造的新数据,所以讲的详细些

作为本场考试 \(Lyin\) 大佬唯一没有一眼秒切的题,这题还是有点意思的

这边顺便讲一下新设置的子任务

我觉得我很良心了,比原题至少多送了 \(15\)

并且增加了一些给比状压更劣做法的部分分,以及一个用于启发正解的子任务

  • \(subtask1\)

送您 \(1\) 分,这样应该就没有爆 \(0\) 的了, 不过您需要输出 \(0\)

  • \(subtask2\)

\(dfs\)

dfs
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int maxn = 250;
int n, r;
ld p[maxn];
int d[maxn], cnt[(1 << 20) + 55];
bool vis[maxn];
ld dfs(int round){
	if(round > r)return 0;
	ld pre = 1, ans = 0;
	for(int i = 1; i <= n; ++i)if(!vis[i]){
		vis[i] = 1;
		ans += pre * p[i] * (dfs(round + 1) + d[i]);
		pre *= (1 - p[i]);
		vis[i] = 0;
	}
	ans += pre * dfs(round + 1);
	return ans;
}
void solve(){
	scanf("%d%d",&n,&r);
	for(int i = 1; i <= n; ++i)scanf("%Lf%d",&p[i], &d[i]);
	printf("%Lf\n",dfs(1));
}
int main(){
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}
  • \(subtask3\)

给不会滚动数组的装压,以及记忆化搜索

  • \(subtask4\)

状压 \(f_{i, j}\) 表示第 \(i\) 轮 修复状态为 \(j\) 的期望,转移如下

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double ld;
const int maxn = 250;
int n, r;
ld p[maxn];
int d[maxn];
ld f[2][(1 << 20) + 55];
void solve(){
	scanf("%d%d",&n,&r);
	for(int i = 1; i <= n; ++i)scanf("%lf%d",&p[i], &d[i]);
	int mx = 1 << n;
	for(int i = 0; i <= 1; ++i) for( int j = 0; j < mx; ++j ) f[i][j] = 0;
	for(int round = 1; round <= r; ++round){
		int zt = round & 1;
		for(int fix = 0; fix < mx; ++fix){
			ld pre = 1, ans = 0;
			for(int i = 1; i <= n; ++i)if(!(fix & (1 << (i - 1)))){
				ans += pre * p[i] * (f[1 - zt][fix | (1 << (i - 1))] + d[i]);
				pre *= (1 - p[i]);
			}
			ans += pre * f[1 - zt][fix];
			f[zt][fix] = ans;
		}
	}
	printf("%.10f\n",f[r & 1][0]);
}
int main(){
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i) cerr << i << endl, solve();
	return 0;
}

至此 \(45\) 分在原数据内只有状压的 \(30\)

  • \(subtask5\)

新加入的用于启发正解的部分分,你可以比较方便的算出特定水管段爆掉的概率,我没有具体实现,不过应该能有人想到,而且确实对正解有启发

  • \(subtask6\)

原题存在的部分分,不知道有什么做法

  • \(subtask7\)

我们考虑计算 \(g_i\) 表示在 \(r\) 轮内第 \(i\) 段被修复过的概率

那么 \(ans = \sum_{i = 1}^{n}g_i d_i\)

发现一段水管被修复过,只需要让在他前面的要么被修过,要么没有爆,也就是水流流经该点,那么其实知道了前面的概率剩该点的期望只与剩余轮数有关

那么设 \(f_{i, j}\) 表示在前 \(i\) 个位置,在 \(r\) 轮中修复过 \(j\) 次的期望

那么就可以枚举当前位置是被修复过,还是根本没有爆即可进行转移

那么我们有

\(f_{i, j} = f_{i - 1, j} \times (1 - p_i)^{r - j} + f_{i - 1, j - 1} \times (1 - (1 - p_i)^{r - j + 1})\)

特别的 \(j = 0\) 时没有 \(f_{i - 1, j - 1}\) 到他的转移

那么 \(g_i\) 就非常好求了

\(g_i = \sum_{j = 0}^{n}f_{i - 1, j} \times (1 - (1 - p_i)^{r - j})\)

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int maxn = 255;
int n, r;
db p[maxn], f[maxn][maxn];
db qpow(db x, int y){
	db ans = 1;
	for(; y; y >>= 1, x = x * x)if(y & 1)ans = ans * x;
	return ans;
}
int d[maxn];
void solve(){
	scanf("%d%d",&n,&r);
	for(int i = 1; i <= n; ++i)scanf("%lf%d",&p[i], &d[i]);
	for(int i = 1; i <= n; ++i)
	 	for(int j = 0; j <= r; ++j)
		 	f[i][j] = 0;
	f[1][0] = qpow((1 - p[1]), r);
	f[1][1] = 1 - f[1][0];
	for(int i = 2; i <= n; ++i){
		int up = min(r, i);
		for(int j = 1; j <= up; ++j)
			f[i][j] += f[i - 1][j - 1] * (1 - qpow(1 - p[i], r - j + 1)) + f[i - 1][j] * qpow(1 - p[i], r - j);
		f[i][0] = f[i - 1][0] * qpow(1 - p[i], r);
	}
	db ans = f[1][1] * d[1];
	for(int i = 2; i <= n; ++i){
		int up = min(r, i);
		db g = 0;
		for(int j = 0; j <= up; ++j) g += f[i - 1][j] * (1 - qpow(1 - p[i], r - j));
		ans += g * d[i];
	}
	printf("%.10lf\n",ans);
}
int main(){
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}

后面是数据生成器等杂项,需要的同学自取

rand
#include<bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;

mt19937 rd((ull)(new char) * (ull) (new char));
double p(){
	uniform_real_distribution<>d(0, 1 - 0.00011);
	return d(rd);
}
int sd(int l, int r){
	uniform_int_distribution<>d(l, r);
	return d(rd);
}

void solve(){
	// int n = sd(4, 5), r = sd();
	int n = sd(15, 20), r = sd(10, 20);
	printf("%d %d\n",n, r);
	for(int i = 1; i <= n; ++i)printf("%.4lf %d\n",p(), sd(1, 1000));
	// double pn = p();
	// for(int i = 1; i <= n; ++i)printf("%.4lf %d\n",pn, sd(1, 1000));
}
int main(){
	int t = 2;
	printf("%d\n",t);
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}
system
#include<bits/stdc++.h>

using namespace std;

int main(){

	for(int i = 10; i <= 10; ++i){
		stringstream ss;
		string s;
		ss <<"./rand>" << i <<".in" << endl;
		// string s = "./rand >";
		// s += i + '0';
		// s += ".in";
		ss >> s;
		ss.clear();
		system(s.c_str());
		ss <<"./c<" << i <<".in" <<">" << i <<".out" << endl;
		ss >> s;
		ss.clear();
		/*
		s = "./c < ";
		s += i + '0';
		s += ".in > ";
		s += i + '0';
		s += ".out";*/
		system(s.c_str());
	}
	return 0;
}

\(upd:\) 修改了 \(g\)

posted @ 2022-09-23 18:52  Chen_jr  阅读(126)  评论(13编辑  收藏  举报