Benelux Algorithm Programming Contest 2019 题解

Benelux Algorithm Programming Contest 2019

A Appeal to the Audience

添加链接描述

题解

要想使得总和最大,就要使最大值被计算的次数最多。要想某个数被计算的多,就要使得它经过尽量多
的节点。于是我们的目标就是找到 k 条从长到短的链,这些链互不重合,且一端是叶子节点。
可以通过长链剖分来将这棵树分为 k 条互不相交的长链,然后按照长度分配元素(长度越大,分配给它的元素值越大)。

参考代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int M = N<<1;
typedef long long ll;
/* 邻接表代码 */
int head[N],nex[M],ver[M],tot = 1;
void addEdge(int x,int y){
	ver[++tot] = y; nex[tot] = head[x]; head[x] = tot;
}
/* 快读代码 */
int getInt(){
	int res = 0;
	bool neg = false;
	char c = getchar();
	while(c != '-' && (c > '9' || c < '0')) c = getchar();
	if(c == '-') neg = true, c = getchar();
	while(c >= '0' && c <= '9') res = res*10+c-'0',c = getchar();
	return neg?-res:res;
}

int len[N],son[N],top[N];
//依次为当前子树最大深度、重儿子编号、当前链顶节点编号
void dfs(int x){
	for(int i = head[x];i;i = nex[i]){
		dfs(ver[i]);
		if(len[son[x]] < len[ver[i]]) son[x] = ver[i];
	}
	len[x] = len[son[x]]+1;
	for(int i = head[x];i ;i = nex[i]){//也可以不更新top数组,在此直接压入q1
		if(ver[i] == son[x]) top[son[x]] = top[x];
		else top[ver[i]] = ver[i];
	}
}
int n,k,a[N];
ll ans = 0;
priority_queue<ll> q1,q2;
void solve(){
	top[1] = 1; dfs(1);
	for(int i = 2;i <= n;i++)
		if(top[i] == i) q1.push(len[i]);
	q1.push(len[1]-1);
	while(q1.size()){
		ans += 1ll * q1.top() * q2.top();
		q1.pop(); q2.pop();
	}
	printf("%lld\n",ans);
}
int main(){
	n = getInt(); k = getInt();
	for(int i = 1,x;i <= k;i++) x = getInt(),q2.push(x);
	for(int i = 2,y;i <= n;i++) y = getInt(), addEdge(y+1,i);
	solve();
	return 0;
}

B Breaking Branches

添加链接描述

题解

设 N 表示当前状态先手必败,P 表示先手必胜。
结论:
n 为奇数时为 N 态,n 为偶数时为 P 态。
证明:
当 n = 1,n = 2 时已知结论成立。
如果对于 n <= 2k 结论都成立,
那么对于 n = 2k+1,必然要分为奇数+偶数,
所以 n = 2k+1 必然要分为 N 态 + P 态,即 n = 2k+1 为 N 态。(博弈论有向图游戏)
同理,对于 n = 2k+2,可以将其分为任意两个奇数的和,使其分为 N 态 + N 态,从而 n = 2k+2 为 P态。
故 n = 2k+1 , n = 2k+2 时结论成立。
综上所述,若 n 为奇数,则先手必败,若 n 为偶数,则先手必胜。

参考代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
	cin >> n;
	if(n&1) cout << "Bob" << endl;
	else cout << "Alice\n1" << endl;
	return 0;
}

C Conveyor Belts

添加链接描述

题解

如果想通过组合 a:b 的分割机来实现分割 c:d ,我们可以先来看简单的情况。
如果 a:b = 1:1,也就是说左右儿子所得都是 1/2。
此时仍可以将 c:d 分为两种情况:

c = 1,即目标是实现 1 : d 。
此时可以通过不断将第 i 个分割器和第 i-1 个分割器的右边相连,那么每个分割器左边就可以得到
1/2, 1/4, 1/8, … , 1/2^k 的‘引脚’,化简后可得 1, 2, 4 ,8 , … ,2^k 的可用引脚(k 是 a:b 分割机个
数);于是我们可以利用二进制思想组合出任意想要的数 d,而第 k 个分割机的右边引脚是 1 。
易得 k < 32。
若 c != 1,此时求 c : d ,假设 c <= d。
此时将 c 和 d 都用二进制表示,即用两个分割机 ac, ad 用来接收 c 和 d 的二进制位,如果能够实
现,那么这两个分割机的输出一定是 c/2 , d/2 满足要求。
现在的问题是每个分割机只有两个输出,我们还要用其中一个来构造下一个二进制位,等于我们
只能用一个引脚,于是遇到 c 和 d 的第 fp 位都是 1 时,引脚不够用的情况。
于是我们可以再设“第二层”,这层接收一个二进制位的输入,再将其分成两等分,这样就可以同
时分给 ac 和 ad 了。
综上所述,如果 a : b = 1 : 1,我们可以很简单的就构造出 c : d 。
再来看更一般的情况 a : b 。 此时很容易想到能否通过尽量少的 a:b 分割机组合得到 1:1 的分割机,
那么在草稿纸上简单的化简一下可以得到:
在这里插入图片描述
即通过 3 个 a : b 分割机可以封装成一个 1 : 1 分割机。于是这道题解决了。

参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,c,d,n;
void Connect(int p,int l,int r){
	/* 将第 p 个 1:1 的分割器左边与第 l 个,右边与第 r 个相连 */
	printf("%d %d\n",p*3+1, p*3+2);
	printf("%d %d\n",p*3, l>0?l*3:l);
	printf("%d %d\n",r>0?r*3:r, p*3);
}
void solve(){
	/* 大体将点分为三层:二进制层(1,2,4,..), 二进制层(这层每个二进制可用两次),
	答案层:即k+cnt,k+cnt1 , 它们是用来存放答案的 */
	int low,high,flag = 0,cnt = 0;
	if(c > d) swap(c,d),flag = true; //确保 c < d
	for(int i = 0;i <= 31;i++) if((c>>i)&1 || (d>>i)&1) cnt++;//统计需要多少二进制位
	for(low = 0;low <= 31;low++) //统计最低位与最高位,浪费可耻
		if(((d>>low)&1) || ((c>>low)&1)) break;
	for(high = 31;high >= low;high--) if((d>>high)&1) break;
	int k = high - low + 1,fp = cnt;//fp为临时指针
	printf("%d\n",(k+cnt+2)*3);
    for(int i = 0;i < k;i++){
		int p = high - i,rs = (i==k-1)?0:i+1;
		if((c>>p)&1 || (d>>p)&1) Connect(i,k+(--fp),rs);
		else Connect(i,0,rs);
	}
	for(int i = 0;i <= 31;i++){
		if((c>>i)&1 && (d>>i)&1) Connect(k+(fp++),k+cnt,k+cnt+1);
		else if((c>>i)&1) Connect(k+(fp++),k+cnt,0);
		else if((d>>i)&1) Connect(k+(fp++),0,k+cnt+1);
	}
	Connect(k+cnt,flag?-2:-1,0); Connect(k+cnt+1,flag?-1:-2,0);//输出答案
}
int main(){
	scanf("%d%d%d%d",&a,&b,&c,&d);
	solve();
	return 0;
}

D Deck Randomisation

添加链接描述

题解

置换循环节+扩展中国剩余定理
置换可以表示成num个循环节乘积的形式,我们求出第 i 个循环节的循环长度 p[i] 和第 i 个循环节第一次变成目标顺序的置换次数 r[i]. 可以解释为:
第 i 个循环节经过了 ki * p[i] + r[i] 次的置换可以变成目标顺序。
我们可以得到一个有 num 个同余方程的同余方程组,因为 p[i] 可能并非两两互质,所以要使用扩展中国剩余定理来解同余方程组;
因为Alice和Bob是轮流刷牌,目标状态可能是在A达到的,也可能是在B达到。所以本题分奇偶;

  1. 偶数情况(A和B刷牌次数一样):为了简化置换,所以将A和B两次置换合并,在置换群中有一个定理:设T^k = e,(T为一置换,e为单位置换(映射函数为的置换)),那么k的最小正整数解是T的拆分的所有循环长度的最小公倍数。所根据此定理求出循环节长度 ,同时可以知道 循环节长度*2就是偶数时达到目标状态的答案;
  2. 奇数情况(A比B刷牌次数多一):在偶数情况已经计算出循环节了,只要AB的置换可以变成A的逆即可,到此p[i] 和 r[i] 就都可以求出来了,进行一次CRT,CRT的答案 * 2+1 就是奇数情况的答案。
    最后答案取奇偶分类讨论中,较小的一个作为答案ans,如果ans大于1e12,输出 “huge”,否则输出ans.
    时间复杂度:O(n
    lgn)
    空间复杂度:O(n)

参考代码:

#include <cstdio>
#include <algorithm>
#include <vector>
#define LL long long
#define ll __int128

const int N = 1e5 + 7;
const LL LIM = 1e12;
int n,cnt_vv=0,num = 0;
LL a[N],b[N],ab[N],ba[N],at[N];
ll p[N],r[N],ans1,ans2,ans,pt;
bool bk[N];
std::vector<ll> vv[N];

inline void print(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
		print(x/10);
		putchar(x%10+'0');
}
ll gcd(ll a, ll b){
	return b ? gcd(b,a%b) : a;
}
ll lcm(ll a,ll b){
	return a/gcd(a,b)*b;
}
ll exgcd(ll a,ll b,ll &x, ll &y){
	if(b == 0){
		x = 1;
		y = 0;
		return a;
	}
	ll t = exgcd(b,a%b,x,y);
	ll x0 = x,y0 = y;
	x = y0;
	y = x0-a/b*y0;
	return t;
}
ll even(){
	ll res = 1;
	cnt_vv = 0;
	for(int i = 1; i <= n; ++i){
		int t = i;
		if(bk[t]) continue;
		int l = 0;
		while(!bk[t]){
			bk[t] = true;
			vv[cnt_vv].push_back(t);
			l++;
			t = ab[t];
		}
		++cnt_vv;
		res = lcm(res,l);
		if(res > LIM) return LIM+1;
	}
	return res;
}
bool odd(){
	bool flag = 1;
	for(int i = 0 ; i < cnt_vv; ++i){
		int size = vv[i].size();
		for(int j = 0; j < size; ++j){
			flag = 1;
			for(int k = 0; k < size; k++){
				if(at[vv[i][k]] != vv[i][(j+k)%size]){
   					flag = 0;
					break;
				}
			}
			if(flag){
				p[++num] = size;
				r[num] = j%size;
				break;
			}
		}
		if(!flag) break;
	}
	return flag;
}
ll CRT(){
	bool flag = 0;
	ll x,y,g,x0,rt;
	pt = p[1],rt = r[1];
	for(int i = 2;i <= num; ++i){
		g = exgcd(pt,p[i],x,y);
		if((r[i] - rt) % g){
			flag = 1;
		}else{
			x0 = (r[i]-rt) / g * x % (p[i]/g);
			rt = x0*pt + rt;
			pt = pt / g * p[i];
		}
		rt = (rt % pt + pt) % pt;
	}
	if(flag) return LIM+1;
	else return rt;
}
int main(){
	scanf("%d",&n);
	for(int i = 1; i <= n; ++i) scanf("%lld",a+i),at[a[i]] = i;
	for(int i = 1; i <= n; ++i) scanf("%lld",b+i);
	for(int i = 1; i <= n; ++i) ab[i] = a[b[i]];
	ans1 = even();
	if(odd()){
		ans2 = CRT();
		ans1 *= 2;
		ans2 = ans2 * 2 +1;
		ans = ans1;
	if(ans > ans2) ans = ans2;
	}else{
		ans1 *= 2;
		ans = ans1;
	}
	if(ans > LIM) puts("huge");
	else print(ans);
	return 0;
}

E Efficient Exchange

添加链接描述

题解

考虑 1254x , 令a = 12540, b = 12550,
可得 1254x = 12540+x或者 12550 - 10 + x,
所以 1254x 最少兑换货币 = Min(a+x, b+10-x)
在这里插入图片描述
只需设二维dp数组,一维表示当前推到数字的哪一位
二维为0表示在当前这一位时的最低货币值,为1表示当前位+1所需的最低货币值
例如83,dp[1, 0] 表示 80 所需最低货币,dp[1, 1]表示90所需最低货币
可得递推式
dp[i+1][0]=min(dp[i][0]+s[i]-‘0’,dp[i][1]+10-s[i]+‘0’)
dp[i+1][1]=min(dp[i][0]+s[i]-‘0’+1,dp[i][1]+10-s[i]+‘0’-1)

参考代码:

#include<iostream>
#include<cstring>
using namespace std;
int dp[10005][2];
int main(){
	char s[10005];
	scanf("%s",s);
	dp[0][1]=1;
	dp[0][0]=0;
	for(int i=0;i<strlen(s);i++){
		dp[i+1][0]=min(dp[i][0]+s[i]-'0',dp[i][1]+10-s[i]+'0');
		dp[i+1][1]=min(dp[i][0]+s[i]-'0'+1,dp[i][1]+10-s[i]+'0'-1);
	}
	printf("%d",dp[strlen(s)][0]);
	return 0;
}

F Find my Family

添加链接描述

题解

题目意思很直白,找出存在中间的数比左边的数小,右边的数比左边的数大,如果对于暴力求法,在 n次情况下必定超时,此时就需要两个客观意义上的指针。首先解决右边的数,那么我们把第 i 个数以后最大的数找出,就可以解决右边数的最大值,剩下的就是左边的数,我们可以同过 set 和二分来查找出比当前 数恰好大一点的数, 若有,再用此数 和右边的最大数相比即可。这样最后的时间复杂度是O(nlogn)。

参考代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+7,INF = 0x3f3f3f3f;
int k,n,Max;
int a[N],max_bh[N];
vector<int> ans;
int main(){
	scanf("%d",&k);
	for(int i=1;i <= k;i++){
		scanf("%d",&n);
		for(int j=1;j <= n;++j) scanf("%d",a+j);
		for(int j=n;j > 0; --j)
		if(a[j] > Max) max_bh[j] = a[j] , Max = a[j];
		else max_bh[j] = Max;
		
		set<int> st;
		Max = 0;
		for(int j=1;j <= n;++j){
			if(!st.size() || a[j] == max_bh[j]){
				st.insert(a[j]); continue;
			}
			auto pos = st.upper_bound(a[j]);
			if(pos != st.end()){
				if(*pos < max_bh[j]){
					ans.push_back(i);
					break;
				}
			}
			st.insert(a[j]);
		}
	}
	printf("%d\n",ans.size());
	for(int i=0;i < ans.size(); ++i)
	printf("%d\n",ans[i]);
	return 0;
}

G Gluttonous Goop

添加链接描述

题解

每过一 time step,真菌就会向其相邻的八个格子蔓延。求经过k个time steps后,被真菌所占的格子的总数。
1 ≤ r、c ≤ 20,0 ≤ k ≤ 1e6。最大结果:(2 * 10^6 + 20)^2 ≈ 4 * 10^12,在 long long范围内
(1)K <= 20时,模拟真菌蔓延
(2)k > 20时,填补法,计算边框的长、宽,用大矩形的面积减去边角的面积。边角的面积就是模拟20次真菌蔓延后,使用填补法添加的面积。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

参考代码:

#include <cstdio>
#include <iostream>
typedef long long ll;
const int N = 66;
bool form[N][N];
ll cnt, res = 1;

int main() {
	int r, c, k;
	scanf("%d%d%d", &r, &c, &k);
	char s[c];
	int num = k>20?20:k;// 模拟的步数
	for (int i = 0; i < r; ++i) {
		scanf("%s", s);
		for (int j = 0; j < c; ++j)
			if (s[j] == '#') {
				form[i+20][j+20] = true;
				for (int x = -num; x <= num; ++x)
					for (int y = -num; y <= num; ++y)
						form[i+20+x][j+20+y] = true;
			}
		}
		int minr=N, minc=N, maxr=0, maxc=0;
		for (int i=20-num, t=r+20+num; i < t; ++i)
			for (int j=20-num, tt=c+20+num; j < tt; ++j) {
				if (form[i][j] == true) {// 计数并找边框的长、宽
					cnt ++;
					minr = std::min(minr, i);
					minc = std::min(minc, j);
					maxr = std::max(maxr, i);
					maxc = std::max(maxc, j);
				}
			}
			
		if (cnt && k>20) {// (2)
			ll h = maxr - minr + 1;
			ll w = maxc - minc + 1;
			ll miss = h*w - cnt;
			res *= h+(k-num<<1);
  		    res *= w+(k-num<<1);
			printf("%lld", res - miss);
		} else
			printf("%lld", cnt);
		return 0;
}

H Historic Exhibition

添加链接描述

题解

建立两个 unordered_map<int, stack > mp1, mp2;对底座的直径和序号进行存储,如果底座的
上下直径相同,将该直径存入mp1中,否则,选取较小的直径存入mp2中,存储方式为 mpx[较小的直径].push(该底座的序号);
在输入花瓶的直径时,将花瓶的序号和对应的直径都存入结构体中,便于排序,将花瓶按照直径从小到大排序,如果直径相等,按照花瓶的序号从小到大排序;(排序是为了在遍历花瓶的直径时,从小到大的去匹配适合的底座,做到不遗漏)
然后,依次遍历 v 个花瓶,分别判断 1. 该花瓶的直径是否在 mp1 2. 该花瓶的直径减一是否在mp2中存在 3. 该花瓶的直径是否在mp2中存在;如果存在,此时该花瓶的底座就是 mpx[该花瓶的直径] 的顶部元素(包含该直径的底座的序号),保存到数组 ans[] 中,下标为花瓶的序号,表示该花瓶放在序号为ans[花瓶的序号] 的底座上,之后去除顶部元素;否则,输出 impossible,程序运行结束。
ans[] 中的元素即为所求解

参考代码:

#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<stack>
const int MAXN = 1e4+10;
int ans[MAXN];
struct node{
	int x, id;
	friend bool operator < (node a, node b){
		return a.x==b.x ? a.id<b.id : a.x<b.x;
	}
}va[MAXN];
std::unordered_map<int,std::stack<int> > mp1, mp2;
int main(){
	int p, v;
	scanf("%d%d", &p, &v);
	for(int i = 1, a, b; i <= p; i++){
		scanf("%d%d", &a, &b);
		if(a == b) mp1[a].push(i);
		else mp2[std::min(a, b)].push(i);
	}
	for(int i = 1, x; i <= v; i++){
		scanf("%d", &x);
		va[i].id = i, va[i].x = x;
	}
	std::sort(va+1, va+1+v);
	for(int i = 1; i <= v; i++){
		if(!mp1[va[i].x].empty()){
			ans[va[i].id] = mp1[va[i].x].top();
			mp1[va[i].x].pop();
		}
		else if(!mp2[va[i].x-1].empty()){
			ans[va[i].id] = mp2[va[i].x-1].top();
   			 mp2[va[i].x-1].pop();
		}
		else if(!mp2[va[i].x].empty()){
			ans[va[i].id] = mp2[va[i].x].top();
			mp2[va[i].x].pop();
		}
		else{
			printf("impossible\n");
			return 0;
		}
	}
	for(int i = 1; i <= v; i++)
		printf("%d\n",ans[i]);
	return 0;
}

I Inquiry II

添加链接描述

题解

在这里插入图片描述

参考代码:

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>

using namespace std;

const int N = 240;
typedef pair<int, int> PII;
int n, m, cnt, idx;
int head[N], ne[N], to[N];
int fa[N], spe[N][2], dp[N][2];
bool vis[N];
void add(int u, int v){
	to[++idx] = v, ne[idx] = head[u], head[u] = idx;
}
int Get(int x){
	if(x != fa[x]) fa[x] = Get(fa[x]);
	return fa[x];
}
void Merge(int x, int y){
	fa[Get(x)] = Get(y);
}
bool is_Same(int x, int y){
	if(Get(x) == Get(y)) return true;
	return false;
}
void tp(int u,int fa){
	if(!vis[u]) dp[u][1] = 1;
	for(int i = head[u]; ~i; i = ne[i]){
		int v = to[i];
		if(v == fa) continue;
		tp(v, u);
		dp[u][0] += max(dp[v][1], dp[v][0]);
		dp[u][1] += dp[v][0];
	}
}
int main(){
	memset(head, -1, sizeof head);
	scanf("%d%d", &n, &m);
	int k = m-n+1;
	for(int i = 1; i <= n; i++) fa[i] = i;
		while(m--){
			int u, v;
			scanf("%d%d", &u, &v);
			if(is_Same(u, v)){
				spe[cnt][0] = u, spe[cnt][1] = v;
				cnt++;
			}
			else{
				add(u, v);
				add(v, u);
				Merge(u, v);
			}
		}
		int ans = 0;
		for(int t = 0; t < (1<<k); t++){
			memset(vis, false, sizeof vis);
			memset(dp, 0, sizeof dp);
			for(int i = 0; i < k; i++) vis[spe[i][(t>>i)&1]] = true;
				tp(1, -1);
				ans = max(ans, max(dp[1][0], dp[1][1]));
		}
		printf("%d\n", ans);
		return 0;
}

J Jazz it Up!

添加链接描述

题解

此题题目要求, 的因子中不能含有平方形式,因为题目中已经说明 是一个无平方因子的数,
那么只要 是无平方因子的数,并且 和 没有共同的因子即可。
根据算术基本定理, 可以分解成若干个质数的积,所以 就直接可以是非 的因子的一个质数。
时间复杂度:取决于你的素数筛法

参考代码:

#include<cstdio>
#include<cstring>
#define ll long long

const ll N=1e5+7;
ll prime[N],ans,n,prime_tot = 0;
bool prime_tag[N];

void get_prime(){
	for(int i = 2; i < N; i++){
		if(!prime_tag[i]){
			prime[prime_tot++] = i;
		}
		for(int j = 0; j < prime_tot && i * prime[j] < N; j++){
			prime_tag[i * prime[j]] = true;
			if(i % prime[j] == 0) break;
		}
	}
	prime_tag[1] = true;
}
int main(){
	get_prime();
	scanf("%lld",&n);
	for(ll i=0;i<prime_tot;i++){
		if(n%prime[i]){
			ans=prime[i];
			break;
		}
	}
	printf("%lld\n",ans);
}

K Keep Him Inside

添加链接描述

题解

本题是一道构造几何的问题,问题可以转变为给巫师所在的每个点一个权重,使得这些点的加权和等于P,也就是要满足下面这个公式,v表示每个点的权值,s表示每个点的坐标
在这里插入图片描述
这个点在凸多边形内部,只要找到该点所在的三角形,使得三角形满足上面的条件,其他的点的权值为0即可,那么利用向量分解,可以转换成两个方程:
在这里插入图片描述
a,b,1-a-b分别代表三角形三个顶点的权值,对面上面的方程继续化简可以得到:
在这里插入图片描述
进一步化简:
在这里插入图片描述
这样就可以利用三角形三点的坐标得出a,b的值,再次化简:
在这里插入图片描述
可以发现a,b前面的系数都是一样的,那么就可以根据上面的推导过程写出程序,可以以巫师所在的第一个点作为基础,然后每次按照上面的公式进行计算,下面看代码:

参考代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e4+5;
ll xa,xb,xc,ya,yb,yc,px,py;
double a,b,c,ans[maxn];
int n;

struct node //用来记录巫师所在点的坐标
{
	ll x,y;
}no[20];
int main()
{
	cin >> n >> px >> py; //px,py表示P点的坐标
	for(int i = 0 ; i < n ; i++)
		cin >> no[i].x >> no[i].y;
		xc = no[0].x,yc = no[0].y;
		xb = no[1].x,yb = no[1].y;
		px -= xc,py -= yc;
		xb -= xc,yb -= yc;
		for(int i = 2 ; i < n ; i++)
		{
			xa = xb,ya = yb;
			xb = no[i].x,yb = no[i].y;
			xb -= xc,yb -= yc;
			double f = xb*ya - xa*yb;//表示上述公式中a,b前面的系数
			a = (xb*py - px*yb)/f;
			b = (px*ya - py*xa)/f;
			c = 1 - a - b;
			if(a >= 0 && b >= 0 && c >= 0)//要保证计算出的权值全为正,并记录在ans中
			{
				ans[0] = c; //c对应的是x3也就是这里的xc,对应点的下标为0
				ans[i-1] = a; //a对应的是x1也就是这里的xa,对应点的下标为i-1
				ans[i] = b; //b对应的是x2也就是这里的xb,对应点的下标为i
			}
		}
		for(int i = 0 ; i < n ; i++)
			cout << setprecision(12) << ans[i] << endl;
		return 0;
}

L Lucky Draw

添加链接描述

题解

根据题意不难得出,游戏最后只有两个结局,平局或者有人胜出,所以可把求平局概率的问题转化为求有人胜出概率的问题。每个人life数相同,所以求出其中某1个人胜出的概率乘n就是有人胜出的概率。
由于存在多人一直抽不到反面的情况,所以我们取一个比较大的回合数(1000足矣),保证误差小于题目给出的范围。某1人胜出的条件为在当前回合只剩他一个,所以求出他在第i回合死并且其他人在第i回合之前死的概率即可,每个人在每个回合死亡的概率都相同,所以实际上其他人在第i回合之前死的概率等于我们所求这某1个人,可得以下的式子:
在这里插入图片描述

参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e3;
long double c[MAXN+10][MAXN+10];
void init(){
	c[0][0]=1;
	for(int i=1;i<=MAXN;i++){
		c[i][0]=c[i][i]=1;
		for(int j=1;j<i;j++){
			c[i][j]=c[i-1][j]+c[i-1][j-1];
		}
	}
}
int main(){
	init();
	int n,k;
	double p;
	scanf("%d%d",&n,&k);
	scanf("%lf",&p);
	double cur=0;
	double tot=0;
	double ans=1;
	for(int i=1;i<=MAXN;i++){
		if(i>=k){
		double P=c[i-1][k-1]*pow(p,i-k)*pow(1-p,k);
		tot=tot+P*pow(cur,n-1);
		cur=cur+P;
		}
	}
	ans=1-n*tot;
	printf("%.6lf\n",ans);
	return 0;
}

如果觉得还行的话,可以三连支持一波(orz)

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值