2020牛客暑期多校训练营(第九场)

总结:
这一场过了A题,之前用C++各种写,但是没想到用py三行代码就解决了问题,还是需要学习很多很多。

K

题意

土拨鼠在第1个宿舍,橙子在第n个宿舍。这n个宿舍间有n-1条路并且长度都为1,土拨鼠从第1个房间去第n个宿舍,速度为1m/s;橙子从第n个宿舍追赶土拨鼠,速度为2m/s。。

思路

二分时间 t ,然后判断在 ts 内土拨鼠是否会被橙子追上。以橙子所在的寝室 n 为根建树,从 1 到 n 枚举所有土拨鼠能够到达的点,先找出t秒能走到哪个点,然后再找这个点能走到的离n最远的点,判断在走的过程中会不会被追上。

代码

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+10;
vector<int> vv[maxn];
int vis[maxn];
int f[maxn];
int dep[maxn];

void dfs(int x,int fa,int de)
{
    f[x] = fa;
    dep[x] = de;
    for (int i =0 ; i< vv[x].size(); i ++ )
    {
        int v = vv[x][i];
        if(v == fa)
            continue;
        dfs(v,x,de + 1);
        vis[x] |= vis[v];
    }
}
int ans =0 ;
void dfs2(int x,int fa,int de)
{
    ans = max(ans,de);
    for (int i =0 ; i<vv[x].size(); i ++ )
    {
        int v = vv[x][i];
        if(v == fa)
            continue;
        dfs2(v,x,de + 1);
    }

}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for (int i = 1; i < n; i ++ )
    {
        int x,y;
        scanf("%d%d",&x,&y);
        vv[x].push_back(y);
        vv[y].push_back(x);
    }
    vis[1] = 1;
    dfs(n,0,1);
    int k = -1;
    int s = dep[1] - m;
    for (int i = 1; i <= n; i ++ ){
        if(vis[i] && dep[i] == s){
            k = i;
            break;
        }
    }
    int num = dep[k] - 1;
    dfs2(k,f[k],1);
    ans -- ;
    num += ans;
    int r = (num + 1) / 2;
    int l = 0;
    while(l < r){
        int mid = l + r>> 1;
        int x = min(2 * mid,num);
        int y = min(mid, ans);
        if(x - dep[k] + 1>= y) r = mid;
        else l = mid + 1;
    }
    printf("%d\n",l);
}

F

题意

土拨鼠要和苹果约会,第 i 天它的第 j 件衣服的邋遢度为 aij ,它要在n天中选择m天和苹果约会,要求选出的m天衣服的邋遢度的最大值和最小值的差值最小。

思路

因为要最小化最大值和最小值的差值,所以用pair存每件衣服的邋遢度和第几天,然后对邋遢度从小到大排序。那么问题就转换成了求一个区间 [L,R] ,使得这个区间覆盖m个不同的天,并且R的邋遢度-L的邋遢度最小。这个问题就可以用尺取解决了。对于每一个L,求出最小的合法的R,每次更新差值最小值即可。

代码

#include<bits/stdc++.h>
#define st first
#define sd second
#define ll long long
#define pii pair<int,int>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 2e6+10;

pii pp[maxn];
int vis[maxn];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int cnt = 0;
    for (int i = 1; i <= n; i ++ ){
        int p;
        scanf("%d",&p);
        for (int j = 1; j <= p; j ++ ){
            int x;
            scanf("%d",&x);
            pp[++cnt].st = x;
            pp[cnt].sd = i;
        }
    }
    sort(pp + 1, pp + 1 + cnt);
    int num = 0;
    int l = 1, r = 1;
    int minn = pp[1].st, maxx = 0;
    int ans = inf;
    while(1){
        while(r <= cnt && num < m){
            if(vis[pp[r].sd] == 0) num ++ ;
            vis[pp[r].sd] ++ ;
            maxx = pp[r].st;
            r ++ ;
        }
        if(num < m)
            break;
        ans = min(ans, maxx - minn);
        vis[pp[l].sd] -- ;
        if(vis[pp[l].sd] == 0) num -- ;
        minn = pp[l + 1].sd;
        l ++ ;
    }
    printf("%d\n",ans);
}

I

题意

给出n个数(0<=a[i]<=9),由这n个数组成2个数,使得这两个数的乘积最小。

思路

自己造几个例子就可以看出,选择0之外最小的数和剩下的数字组成的没有前导0的数相乘结果最小。剩下的数字怎样最小呢,当然是找到最小的非零的数做第一位,后边接上所有的0,再按照有小到大的顺序放剩下的数字。因为n的范围在1e5,所以要用高精度乘法。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int a[100100];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=0;i<n;i++) cin>>a[i];
        sort(a,a+n);
        int k=0;
        while(k<n&&a[k]==0) k++;
        vector<int>q;
        q.push_back(a[k+1]);
        for(int i=0;i<k;i++) q.push_back(0);
        for(int i=k+2;i<n;i++) q.push_back(a[i]);
        vector<int>ans;
        for(int i=q.size()-1;i>=0;i--){
            int x=q[i]*a[k];
            ans.push_back(x);
        }
        for(int i=0;i<10;i++) ans.push_back(0);
        for(int i=0;i<ans.size();i++){
            if(ans[i]>=10){
                ans[i+1]+=ans[i]/10;
                ans[i]%=10;
            }
        }
        while(!ans.back()) ans.pop_back();
        for(int i=ans.size()-1;i>=0;i--) cout<<ans[i];
        cout<<endl;
    }
    return 0;
}

一级目录

题意

思路

代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值