2020 KAIST 10th ICPC Mock Contest部分题目解答

题目链接 https://codeforces.com/gym/102760

B

首先计算出 Donghyun 可以抗 \(t=\lceil \frac{c}{5} \rceil\) 次伤害。

如果 t>b ,那就死不了,存活概率 \(1\)

如果 t<=b ,总情况有 \(C_a^b\) 种, 而保证 Donghyun 死掉的情况有 \(C_{a-b}^{b-t}\) 种,故存活的概率为

\[1-\frac{C_{a-b}^{b-t}}{C_a^b} = 1-\prod_{i=1}^{t}\frac{b-i+1}{a-i+1} \]

代码:

#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;

int a,b,c;
int main(){
    cin>>a>>b>>c;
    int t=(c%5==0?c/5:c/5+1);
    if(t>b) cout<<1<<endl;
    else{
        long double ans=1;
        for(int i=1;i<=t;i++) ans=ans*(b-i+1)/(a-i+1);
        cout<<setprecision(10)<<1-ans<<endl;
    }
    return 0;
}

D

记所求的值分别为 minv,maxv ,前者很好求,就是对排序,最小生成树就是用最短的 n-1 条边得到的,所得权值显然最小。

但是后者并不能取最长的 n-1 条边,因为对应的最小生成树无法保证是用它们连成的。

那么大致的思想是让比较小的边尽量组成完全图,这样它们就不足以得到生成树。

具体来讲就是对边进行排序,(假设给了一个 \(n\) 阶完全图)然后先让小边组成 \(n-1\) 阶完全图,这样大边就可以选出最小的那条去得到生成树。
这样一来,我们就可以按照这个策略下去,让小边按顺序组成 \(1,2,3...n-1,n\) 阶完全图。(可以用反证法证明没有更好的策略了。)

那么生成树权值和就是由区间[1,1],[2,3],[3,6],...[(n-1)*(n-2)/2+1,n*(n-1)/2]中的最小值求和得来。

#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;
#define SET0(a) memset(a,0,sizeof(a))
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define DWN(i,a,b) for(int i=(a);i>=(b);i--)
#define INF 0x3f3f3f3f
typedef long long ll;

const int N=100,M=N*N;
int n;
int a[M];

int main(){
    cin>>n;
    FOR(i,1,n*(n-1)/2) cin>>a[i];

    sort(a+1,a+1+n*(n-1)/2);
    
    ll minv=0,maxv=0;

    FOR(i,1,n-1) minv+=a[i];

    int s=1;
    FOR(i,1,n-1)
    {
        s+=i-1;
        maxv+=a[s];
    }

    cout<<minv<<' '<<maxv<<endl;
    return 0;
}

F

这题一眼看过去就是二分答案,枚举起点,然后二分求出相应起点对应正方形的边长最大值,不断更新就好了。
在二分check的时候需要知道区间的最小值,故用ST表维护。

总的时间复杂度是 \(O(N\log{N})\)

好像还可以用单调栈来做,时间复杂度是 \(O(N)\),但我不熟悉太弱了

#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;
#define SET0(a) memset(a,0,sizeof(a))
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define DWN(i,a,b) for(int i=(a);i>=(b);i--)
#define INF 0x3f3f3f3f
typedef long long ll;

const int N=3e5+5,M=20;
int a[N];
int n;

//st
int st[N][M];
int Log[N];
void pre(){
    Log[1]=0;
    FOR(i,2,N-1) Log[i]=Log[i/2]+1;
}

void init(){
    FOR(j,0,M-1)
        for(int i=1;i+(1<<j)-1<=n;i++)
            if(!j) st[i][j]=a[i];
            else st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}

int query(int l,int r){
    int s=Log[r-l+1];
    return min(st[l][s],st[r-(1<<s)+1][s]);
}

//binary checker
bool check(int cur,int len){
    return len<=query(cur,cur+len-1) && cur+len-1<=n;
}

int main(){

    cin>>n;
    FOR(i,1,n) cin>>a[i];

    //build st
    pre();
    init();

    int ans=1;
    FOR(i,1,n){
        int l=0,r=n;
        while(l<r){
            int mid=l+r>>1;
            if(check(i,mid+1)) l=mid+1;
            else r=mid;
        }
        ans=max(ans,l);
    }

    cout<<ans<<endl;

    return 0;
}

H

一开始我还读错题意了,以为是贪心,事实上不是orz,这题就是一个暴力,枚举 六种物品 取与不取即可,共 \(2^6\) 种策略。

#include<bits/stdc++.h>

using namespace std;

const int N=1e5+5;
int v[7],a[N];
int ans=0;
int n,k;
bool choose[64][7];
 // 记录策略
int main(){
    cin>>n>>k;
    for(int i=1;i<=6;i++) cin>>v[i];
    for(int i=1;i<=n;i++) cin>>a[i];

    for(int i=0;i<(1<<6);i++){
        for(int j=1;j<=6;j++){
            choose[i][j]= (i>>j-1)&1;
        }
    }

    for(int i=0;i<(1<<6);i++){
        int t=k;
        int rec=0;
        for(int j=1;j<=n;j++){
            if(choose[i][a[j]] && t>=v[a[j]]){
 // 模拟能否选取,如果能则必须选。
                t-=v[a[j]];
                rec++;
            }
        }
        ans=max(ans,rec);
    }
    cout<<ans<<endl;

    return 0;
}

K

构造题,可以先放在一维上思考,此时的策略是 \({1,2,3...,n-1,n,n-1,...3,2,1}\)

推广到平面:
想到将 \(x坐标\) 为第一关键字, \(y坐标\) 为第二关键字排序,尽量化为一条线处理,此时类似地连边即可。

#include<bits/stdc++.h>

using namespace std;

const int N=1005;
int n;
struct node{
    int x,y;
    int id;
}p[N];

bool cmp(node a,node b){
    if(a.x<b.x) return true;
    if(a.x==b.x) return a.y<b.y;
    return false;
}
int main(){
    cin>>n;

    for(int i=1;i<=n;i++){
        cin>>p[i].x>>p[i].y;
        p[i].id=i;
    }

    sort(p+1,p+1+n,cmp);

    cout<<2*n-1<<endl;
    
    for(int i=1;i<=n;i++) cout<<p[i].id<<' ';
    for(int i=n-1;i;i--) cout<<p[i].id<<' ';
    cout<<endl;

    return 0;
}
posted @ 2021-02-12 20:32  HinanawiTenshi  阅读(156)  评论(0编辑  收藏  举报