2020牛客寒假算法基础集训营6

传送门

A.配对

题意:

  现在有正整数集合 A 和 B,每个集合里有 N 个数,你要建立他们间的一一映射

将每对配对的数字相加可以得到 N 个和,你要做的就是最大化第 K 大的和

1≤K≤N≤100,000 输入的所有数字不超过 108

思路:

  我们要使得第K大的和尽可能大,显然可以贪心:首先,组成这K对数字的显然是A中最大的K个数字和B中最大的K个数字。

问题转化为怎样配对使得最小的和最大:我们发现,如果A1<A2,B1<B2,那么一定是由A1和B2配对较优。

经过简单的归纳可以得到,倒序配对是最优的,这样就解决了问题。

#include<bits/stdc++.h>
using namespace std;
const int N =1e5+10;
int a[N],b[N],sum[N];

int n,k;
int main(){
    cin>> n >> k;
    for(int i=1;i<=n;i++) cin>> a[i];
    for(int i=1;i<=n;i++) cin>> b[i];
    sort(a+1,a+1+n,greater<int>());
    sort(b+1,b+1+n,greater<int>());
    int ans =2e8+17;
    for(int i=1;i<=k;i++)
        ans =min(ans,a[i]+b[k+1 - i]);
    cout<<ans<<endl;
    return 0;
}
Code

B.图

题意:

  现在有一个N个点的有向图,每个点仅有一条出边

你需要求出图中最长的简单路径包含点的数量。

思路:

  由题意可知,这是一个基环树。关于这类题。我们可以先找出环,把环的相关点标记上环的长度。

那么简单路径就是除环外的点到环的距离加上环的长度

#include<bits/stdc++.h>
using namespace std;
const int N =1e6+17;
int vis[N],to[N],h[N];
int n,ans;

void dfs(int id){
    if(h[to[id]] == 0){
        int res = 1;
        int k =to[id];
        while(k != id){
            res++;
            k = to[k];
        }
        h[k] = res;
        vis[k] = 1;
        k = to[k];
        while(k != id){
            h[k] =res;
            vis[k] =1;
            k =to[k];
        }
        return ;
    }
    h[id] =0;
    if(vis[to[id]] == 0){
        dfs(to[id]);
        if(vis[id] == 0){
            h[id] = h[to[id]]+1;
            vis[id] =1;
        }
    }
    else if(vis[to[id]] ==1) {
        h[id] = h[to[id]]+1;
        vis[id] =1;
        return ;
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>to[i];
    for(int i=1;i<=n;i++) h[i] =-1;
    for(int i=1;i<=n;i++){
        if(vis[i] == 0) dfs(i);
        ans =max(ans,h[i]);
    }
    cout<<ans<<endl;
    return 0;
}
Code

 

D.重排列

题意:  

  一个序列的重排列是指对这个序列中的元素进行若干次(包括0次)交换操作后得到的新序列.

在本题中,序列中可能出现重复的数字,他们被视作不同的元素.

  现在有两个长度为 N 的非负整数序列 A 和 B,问有多少种 A 的重排列满足对于所有的 1≤i≤N,有Ai≤Bi

由于答案可能很大,你只需要输出答案对1e9+7取模的结果.

思路:

  将a,b排序,双指针模拟一下。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+17;
const int mod = 1e9+7;
int a[N],b[N],n;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)cin>>b[i];
    sort(a+1,a+1+n);
    sort(b+1,b+1+n);
    long long ans =1;
    for(int i=1,j=0;i<=n;i++){
        while(j<n&& a[j+1] <=b[i]) j++;
        ans = ans * max(0LL,(j-i+1LL)*1LL)%mod;
    }
    ans =(ans+mod)%mod;
    cout<<ans<<endl;
    return 0;
}
Code

E.立方数

题意:对于给定的正整数 N,求最大的正整数 A,使得存在正整数 B,满足 A3B=N;

思路:先对N质因数分解。用N去对质因数试除,最后用二分法来验证余下的X是不是一个完全立方数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e4+5;
int vis[maxn],p[maxn],tot;
ll n,cnt,ans,l,r,mid;
int main()
{
    for(int i=2;i<maxn;i++)
    {
        if(!vis[i])
        {
            p[tot++]=i;
            for(int j=i*2;j<maxn;j+=i)
                vis[j]=1;
        }
    }
    int t;cin>>t;
    while(t--)
    {
        cin>>n;
        ans=1;
        for(int i=0;i<tot;i++)
        {
            if(n%p[i])continue;
            cnt=0;
            while(n%p[i]==0)
            {
                n/=p[i];
                cnt++;
                if(cnt==3)
                {
                    ans*=p[i];
                    cnt=0;
                }
            }
        }
        l=1;r=1e6;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(mid*mid*mid==n)
            {
                ans*=mid;
                break;
            }
            else if(mid*mid*mid<n)
            l=mid+1;
            else r=mid-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}
Code

 

F.十字阵列

题意:

  小 Q 新学会了一种魔法,可以对一个 N行M列 的网格上的敌人造成伤害

第 i 次使用魔法可以对网格上的一个十字形区域(即第 xi 行和第 yi 列的并)中的每个格子上的敌人造成 zi 点伤害

现在小 Q 一共使用了 H 次魔法,你需要在所有的施法完成之后统计造成伤害的情况,详见输出描述

思路:

  设3个数组a[],b[]和ab[][],分别统计对行,列和每格造成的伤害

进行操作(xi,yi,zi)时,a[xi],b[yi],ab[xi][yi]都加上zi

每次询问一格的答案时,只要查询a[x]+b[y]-ab[x][y]就好了

#include<bits/stdc++.h>
using namespace std;
const int N= 2010;
const int mod = 1e9+7;
int x[N],y[N],xy[N][N],n,m,h;
int main(){
    cin>>n>>m>>h;
    long long ans =0;
    while(h--){
        int i,j,v;
        cin>>i>>j>>v;
        x[i]+=v,y[j]+=v,xy[i][j]+=v;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
             ans = (ans + (long long)(x[i] + y[j] - xy[i][j]) * (i + j) % mod) % mod;
    cout<<(ans+mod)%mod<<endl;
    return 0;
}
Code

G.括号序列

题意:

  合法括号序列的定义是:

1.空序列是合法括号序列

2.如果 S 是一个合法括号序列,那么(S)是合法括号序列

3.如果 A 和 B 都是合法括号序列,那么 AB 是一个合法括号序列

现在给定一个括号序列,求最少删去几个括号能得到一个合法的括号序列

 

思路:
  用栈模拟,栈的大小就是需要操作的个数。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
char s[N];
int main(){
    int t;cin>>t;
    while(t--){
        int n;cin>>n;
        scanf("%s",s+1);
        stack<int> st;
        for(int i=1;i<=n;i++){
            if(!st.empty()){
                char t = s[st.top()];
                if( t == '(' && s[i] == ')') st.pop();
                else st.push(i);
            }
            else st.push(i);
        }
        cout<<st.size()<<endl;
    }
    return 0;
}
Code

J.签到题

题意:

  现有一个边长为正整数的三角形,问能否以其三个顶点为圆心画三个圆,使三个圆两两外切

三边长均不超过108。

思路:

  三角形三边长a,b,c,三个顶点的圆半径为x,y,z.则 x+y =a,x+z =b; y+z =c .解出x,y,z.

作者:珩月
链接:https://ac.nowcoder.com/discuss/367149?type=101&order=0&pos=1&page=2
来源:牛客网
#include<bits/stdc++.h>
using namespace std;
 
double a[3], b[3];
int main()
{
    cin >> a[0] >> a[1] >> a[2];
    sort(a, a + 3);
    if(a[0] + a[1] <= a[2]){
        puts("wtnl");
        return 0;
    }
    puts("Yes");
    b[1] = (a[2] - a[1] + a[0]) / 2;
    b[0] = a[0] - b[1], b[2] = a[2] - b[1];
    printf("%.2lf %.2lf %.2lf", b[0], b[1], b[2]);
     
    return 0;
}
Code
posted @ 2020-02-17 22:06  NukezY  阅读(189)  评论(0)    收藏  举报