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的更多相关文章

  1. ICPC训练周赛 Benelux Algorithm Programming Contest 2019

    D. Wildest Dreams 这道题的意思是Ayna和Arup两人会同时在车上一段时间,在Ayna在的时候,必须单曲循环Ayna喜欢的歌,偶数段Ayna下车,若此时已经放了她喜欢的那首歌,就要将 ...

  2. 03.21 ICPC训练联盟周赛:UCF Local Programming Contest 2018正式赛

    B Breaking Branches 题意:两个人比赛折枝,谁剩下最后1,无法折出整数即为输 思路:树枝长n,若是奇数,则Bob胜出,若是偶数,则Alice胜出,且需要输出1: 1 #include ...

  3. 03.14 ICPC训练联盟周赛,Preliminaries for Benelux Algorithm Programming Contest 2019

    A .Architecture 题意:其实就是想让你找到两行数的最大值,然后比较是否相同,如果相同输出'possible',不同则输出'impossible' 思路:直接遍历寻找最大值,然后比较即可 ...

  4. ICPC训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019

    I题 求 a 数组平方的前缀和和求 a 数组后缀和,遍历一遍即可 AC代码 #include<iostream>#include<cmath>using namespace s ...

  5. 2020.3.14--训练联盟周赛 Preliminaries for Benelux Algorithm Programming Contest 2019

    1.A题 题意:给定第一行的值表示m列的最大值,第m行的值表示n行的最大值,问是否会行列冲突 思路:挺简单的,不过我在一开始理解题意上用了些时间,按我的理解是输入两组数组,找出每组最大数,若相等则输出 ...

  6. Benelux Algorithm Programming Contest 2019

    J. Jazz it Up!题目要求,n*m的因子中不能含有平方形式,且题目中已经说明n是一个无平方因子的数, 那么只要m是无平方因子的数,并且n和m没有共同的因子即可.要注意时间复杂度!代码:#in ...

  7. Preliminaries for Benelux Algorithm Programming Contest 2019

    A. Architecture 如果行最大值中的最大值和列最大值中的最大值不同的话,那么一定会产生矛盾,可以手模一个样例看看. 当满足行列最大值相同条件的时候,就可以判定了. 因为其余的地方一定可以构 ...

  8. 03.28,周六,12:00-17:00,ICPC训练联盟周赛,选用试题:UCF Local Programming Contest 2016正式赛。

    A. Majestic 10 题意:三个数均大于10则输出"triple-double",如果两个数大于10则输出"double-double",如果一个大于1 ...

  9. 2014 Benelux Algorithm Programming Contest (BAPC 14)E

    题目链接:https://vjudge.net/contest/187496#problem/E E Excellent Engineers You are working for an agency ...

随机推荐

  1. 两种github action 打包.Net Core 项目docker镜像推送到阿里云镜像仓库

    两种github action 打包.Net Core 项目docker镜像推送到阿里云镜像仓库 1.GitHub Actions 是什么? 大家知道,持续集成由很多操作组成,比如抓取代码.运行测试. ...

  2. vue ele table表格 设置只能勾选一个

    table 更改属性设置: <el-table ref="multipleTable" :data="tableData" tooltip-effect= ...

  3. vue之 分页封装

    npm 下载 npm i element-ui -S components 创建 Page 文件夹 创建 Page.vue 文件 vue 文件 <template>   <div c ...

  4. Python之requests模块-response

    response类故名思议,它包含了服务器对http请求的响应.每次调用requests去请求之后,均会返回一个response对象,通过调用该对象,可以查看具体的响应信息. 示例如下: import ...

  5. 离线安装Windows Terminal

    Windows Terminal颜值高.适配好.速度快,是Windows 10下命令行工具的不二选择. 最近在公司电脑上安装Windows Terminal时遇到一个问题,由于公司电脑不能直接连接外网 ...

  6. XXE从0到1

    XXE从0到1 1. XXE概述 XXE(XML External Entity Injection)即XML外部实体注入.漏洞是在对不安全的外部实体数据进行处理时引发的安全问题. 下面我们主要介绍P ...

  7. 一键配置tomcat定期日志清理功能

    概述 日志文件包含了关于系统中发生的事件的有用信息,在排障过程中或者系统性能分析时经常被用到.对于忙碌的服务器,日志文件大小会增长极快,服务器会很快消耗磁盘空间,这成了个问题.除此之外,处理一个单个的 ...

  8. 在 Docker 的 CentOS7 镜像 中安装 mysql

    在 Docker 的 CentOS7 镜像 中安装 mysql 本来以为是个很简单的过程居然折腾了这么久,之前部署云服务器时也没有好好地记录,因此记录下. 特别提醒:本文的操作环境是在 Docker ...

  9. HDU2063 过山车(二分匹配)

    过山车 HDU - 2063 RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了.可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做part ...

  10. mysql事务干货详解

    说明: mysql是现在行业中流行的关系型数据库,它的核心是存储引擎.mysql的存储引擎有很多种我们可以通过命令查看如下 SHOW ENGINES 不同版本得到的数据不一样,我们今天说的事务是在 M ...