2020.3.21--ICPC训练联盟周赛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 分为两种情况:
1).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。
2).若 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刷牌次数多一):在偶数情况已经计算出循环节了,只要A*B的置换可以变成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 <int> > 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
对于一般图求最大独立集,都是利用 算法 建立补图计算最大团。但是本题n的最大
值达到100,对于这样一个指数级别的算法,妥妥地 TLE。我们再来观察一下数据,m的范围为[n-1,n+15].
此时,我们可以发现边的数量非常少,而可以构成环的边只有 条。对于
这m-(n-1)条边的每一条边而言,至少有一个端点是不在最大独立集中。所以,我们可以利用并查集找
到这 条边,利用二进制思想暴力枚举 条边每个端点的是否存在最大独立集中,这样我们可以将环移
除,将问题退化成树的情况,利用树形DP求出每种情况的最大独立集,取最大值。
对于本题的树形DP而言:
设dp[u,0] 表示以u 为根的子树中, u不选,最大独立集中个数的最大值。此时,u 的子节点可选可不选。
设 dp[u,0]表示以 u为根的子树中,u 必选,最大独立集中个数的最大值。此时,u 的子节点不可以选。
本题为连通图,故我们随便定一个点 作为根节点即可,DP的目标为max(dp[root][0],dp[root][1]),由于一开始最大独立集为空,所以 数组全部初始化为0,由于我们要利用二进制枚举 ,所以时间复杂度为 ,树形DP求最大独立集的时间复杂度为O(n),最终总的时间复杂度为O(2^k*n)
#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表示每个点的坐标
v1*s1(x,y)+v2*s2(x,y)+...+vn*sn(x,y)=p(x,y)
这个点在凸多边形内部,只要找到该点所在的三角形,使得三角形满足上面的条件,其他的点的权值为
0即可,那么利用向量分解,可以转换成两个方程:
ax1+bx2+(1-a-b)x3=px
ay1+by2+(1-a-b)y3=py
a,b,1-a-b分别代表三角形三个顶点的权值,对面上面的方程继续化简可以得到:
(x1-x3)a+(x2-x3)b=px-x3
(y1-y3)a+(y2-y3)b=py-y3
进一步化简:m1a+m2b=m; n1a+n2b=n
这样就可以利用三角形三点的坐标得出a,b的值,再次化简:
(m2n1-m1n2)a=m2n-mn2; (m2n1-m1n2)b=mn1-m1n
可以发现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个人,又根据概率论知识,n次独立重复实验某事件发生k次的概率
#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;
}
2020.3.21--ICPC训练联盟周赛Benelux Algorithm Programming Contest 2019的更多相关文章
- ICPC训练周赛 Benelux Algorithm Programming Contest 2019
D. Wildest Dreams 这道题的意思是Ayna和Arup两人会同时在车上一段时间,在Ayna在的时候,必须单曲循环Ayna喜欢的歌,偶数段Ayna下车,若此时已经放了她喜欢的那首歌,就要将 ...
- 03.21 ICPC训练联盟周赛:UCF Local Programming Contest 2018正式赛
B Breaking Branches 题意:两个人比赛折枝,谁剩下最后1,无法折出整数即为输 思路:树枝长n,若是奇数,则Bob胜出,若是偶数,则Alice胜出,且需要输出1: 1 #include ...
- 03.14 ICPC训练联盟周赛,Preliminaries for Benelux Algorithm Programming Contest 2019
A .Architecture 题意:其实就是想让你找到两行数的最大值,然后比较是否相同,如果相同输出'possible',不同则输出'impossible' 思路:直接遍历寻找最大值,然后比较即可 ...
- ICPC训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019
I题 求 a 数组平方的前缀和和求 a 数组后缀和,遍历一遍即可 AC代码 #include<iostream>#include<cmath>using namespace s ...
- 2020.3.14--训练联盟周赛 Preliminaries for Benelux Algorithm Programming Contest 2019
1.A题 题意:给定第一行的值表示m列的最大值,第m行的值表示n行的最大值,问是否会行列冲突 思路:挺简单的,不过我在一开始理解题意上用了些时间,按我的理解是输入两组数组,找出每组最大数,若相等则输出 ...
- Benelux Algorithm Programming Contest 2019
J. Jazz it Up!题目要求,n*m的因子中不能含有平方形式,且题目中已经说明n是一个无平方因子的数, 那么只要m是无平方因子的数,并且n和m没有共同的因子即可.要注意时间复杂度!代码:#in ...
- Preliminaries for Benelux Algorithm Programming Contest 2019
A. Architecture 如果行最大值中的最大值和列最大值中的最大值不同的话,那么一定会产生矛盾,可以手模一个样例看看. 当满足行列最大值相同条件的时候,就可以判定了. 因为其余的地方一定可以构 ...
- 03.28,周六,12:00-17:00,ICPC训练联盟周赛,选用试题:UCF Local Programming Contest 2016正式赛。
A. Majestic 10 题意:三个数均大于10则输出"triple-double",如果两个数大于10则输出"double-double",如果一个大于1 ...
- 2014 Benelux Algorithm Programming Contest (BAPC 14)E
题目链接:https://vjudge.net/contest/187496#problem/E E Excellent Engineers You are working for an agency ...
随机推荐
- 正整数a、b、c、d满足ab=cd,则a+b+c+d必定为合数。
正整数a.b.c.d满足ab=cd,则a+b+c+d必定为合数. 证法一:记s=a+b+c+d.如果四个数全为1,s=4,显然是合数.考虑四个数非全1的情形,由对称性,不妨令a>1. 设p是a的 ...
- 使用Keepalived实现Nginx的双机主备高可用
1.概述 前面我们聊过使用 Nginx 为 后端Tomcat 做负载均衡.高可用,但是这时Nginx又成了单点,如果Nginx不幸挂掉,整个网站便无法访问. 此时我们就会用到另一个软件 -- Keep ...
- IDEA中mybatis generator使用
1.在对应服务的pom.xml文件中添加依赖 <build> <plugins> <plugin> <groupId>org.mybatis.gener ...
- 云效x钉钉:让研发工作更简单
云效x钉钉:让研发工作更简单,奔走相告,云效&钉钉集成实现组织架构.成员同步以及消息通知啦! 我们知道云效致力于智能化.安全可追溯.高效.简单.灵活,「云效新一代企业级DevOps平台」阿里云 ...
- 斐波那契数(Java)
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 .该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0,F(1) = 1 F(n) = F(n ...
- [第五篇]——Docker 镜像加速之Spring Cloud直播商城 b2b2c电子商务技术总结
Docker 镜像加速 国内从 DockerHub 拉取镜像有时会遇到困难,此时可以配置镜像加速器.Docker 官方和国内很多云服务商都提供了国内加速器服务,例如: 科大镜像: 网易: 阿里云: 你 ...
- vue-cli 项目中使用 v-chart 及导出 chart 图片
安装: npm i v-charts echarts -S 组件中使用: 1 <template> 2 <div class="app-chart"> 3 ...
- C++快读讲解
C++快读讲解 inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') ...
- c++ undefined reference
记录一次c++编程时发现的问题 报错 undefined reference undefined reference to `Student::~Student()' 下面还有类似的好几行,翻译过来就 ...
- 数学相关函数在PHP中的应用简介
对于数学计算来说,最常见的其实还是我们使用各种操作符的操作,比如说 +加.-减 之类的.当然,PHP 中也为我们提供了一些可以方便地进行其他数学运算的操作函数.这些函数都属于 Math 扩展.这个扩展 ...