2020牛客寒假算法基础集训营6【A - J】

题目来源:https://ac.nowcoder.com/acm/contest/3007#question
这场又有好多AK的,我tcl… 可能会补2题左右,保证不咕~


A - 配对

先对两个序列从小到大排序,假如选第k大的,那我们肯定不能让第k大的这个碰到两个序列前1 - k 的元素,忽略他们,然后后面的头+尾 排个序取最小值

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,string> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-10;
const double pi=acos(-1);
int n,m;
int f[N],g[N];
int p[N];
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    r(n); r(m);
    FOR(i,1,n) r(f[i]);
    FOR(i,1,n) r(g[i]);
    sort(f+1,f+n+1);
    sort(g+1,g+n+1);
    FOR(i,n-m+1,n){
        p[i-n+m]=f[i]+g[n-i+n-m+1];
    }
    sort(p+1,p+m+1);
    cout<<p[1]<<endl;
    return 0;
}


B - 图

这题应该就是类似于记忆化搜索,搜过的点标记上它的值,然后可以O(n)解决,难点就在于环的处理。但是这样想,从一个点开始搜,它最终的结局要么是撞到自己刚刚搜到的点成环,要么是碰到已经搜过的点把这个点的值+1即可。
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,string> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-10;
const double pi=acos(-1);
int n,m;
int to[N];
bool vis[N];
int step[N];
int num[N];
int ans,loop;
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int dfs(int x,int stp)
{
    //cout<<x<<' '<<stp<<endl;
    step[x]=stp;
    int t=to[x];
    if(vis[t]==0){
        vis[t]=1;
        int res=dfs(t,stp+1);
        if(loop!=0){
            if(loop==x){
                loop=0;
            }
            return num[x]=res;
        }
        else{
            return num[x]=res+1;
        }
    }
    else{
        if(num[t]>0) return num[x]=num[t]+1;
        else if(num[t]==0){
            loop=t;
            if(t==x) loop=0;
            return num[x]=step[x]-step[t]+1;
        }
    }
}
int main()
{
    r(n);
    FOR(i,1,n){
        r(to[i]);
    }
    ans=0;
    FOR(i,1,n){
        if(!vis[i]){
            vis[i]=1;
            loop=0;
            dfs(i,1);
        }
    }
    FOR(i,1,n){
        ans=max(num[i],ans);
       // cout<<num[i]<<' ';
    }
    //cout<<endl;
    cout<<ans<<endl;
    return 0;
}


C - 汉诺塔

这题真的感觉我最近在哪里做过类似的题,应该是cf上!
首先对x从大到小排序,然后从左到右加入set,加之前先看他能不能在set中找到比他的y小的最大的点放在他上面

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,int> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-10;
const double pi=acos(-1);
int n,m;
struct node
{
    int x,y,id;
    bool operator< (node a){
        return x<a.x;
    }
}f[N];
int val[N];
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    r(n);
    FOR(i,1,n){
        r(f[i].x); r(f[i].y);
        f[i].id=i;
    }
    sort(f+1,f+n+1);
    set<pt> s;
    int num=0;
    FOR(i,1,n){
        if(s.size()){
            set<pt>::iterator it=s.lower_bound(mp(f[i].y,0));
            if(it!=s.begin()){
                it--;
                if(val[it->second]==0){
                    val[it->second]=++num;
                    val[f[i].id]=num;
                }
                else{
                    val[f[i].id]=val[it->second];
                }
                s.erase(it);
            }
        }
        s.insert(mp(f[i].y,f[i].id));
    }
    FOR(i,1,n){
        if(val[i]==0) val[i]=++num;
    }
    cout<<num<<endl;
    FOR(i,1,n) cout<<val[i]<<' ';
    cout<<endl;
    return 0;
}

D - 重排列

反着思考,我们找不满足的 和总数相减即可
先对两个序列排序,这不会影响结果(可以理解的吧)
然后只要我们满足任意一个地方大于 他就是不满足的 没问题吧.
遍历一遍,对于当前的点i 我们找比这个点大的有多少个(二分搜索) 然后在这个点用比他大的点,那么后面任意怎么排都行ans=(ans+mult*cnt_h%mod*cc[n-i]%mod)%mod;
但是这个mult是干嘛的? 如果我们选不大于这个点的那就要看下一个点大不大嘛

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,char> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-10;
const double pi=acos(-1);
LL n,m;
int f[N];
int g[N];
LL cc[N];
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    r(n);
    cc[0]=1;
    FOR(i,1,n){
        cc[i]=cc[i-1]*i%mod;
    }
    FOR(i,1,n){
        r(f[i]);
    }
    FOR(i,1,n) r(g[i]);
    sort(g+1,g+n+1);
    sort(f+1,f+n+1);
    LL ans=0,mult=1;
    FOR(i,1,n){
        LL cnt_l=upper_bound(f+i,f+n+1,g[i])-f-i;
        LL cnt_h=(n-i+1)-cnt_l;
        ans=(ans+mult*cnt_h%mod*cc[n-i]%mod)%mod;
        //cout<<ans<<endl;
        if(cnt_h==n-i+1||cnt_h==0){
            break;
        }
        else{
            mult=mult*cnt_l%mod;
        }
    }

    LL res=(cc[n]-ans+mod)%mod;
    cout<<res<<endl;
    return 0;
}


E - 立方数

这题还是没想到那么深…
对于1e4组数据,我们的复杂度基本不能超过1e4 我的复杂度是带了个常数的1e4:首先素数筛,这里的 素数筛不是筛1018 以内的,筛出N1/4 以内的就够用了 具体原因请听我细细道来: A 3 B = N A^3B=N A3B=N 我们只需要对N进行素数分解 然后取其素数的幂/3的结果 即是A 但是不经处理的话 复杂度是1e6 , 我们再仔细想 假如我们取N1/4以内的素数去分解 之后会剩下一个M,这个M里面包含的是N中大于N1/4的素数 (假如有素数的幂超过3 另外还包含其他没超过3的,那这个M=(N1/4+x)3*(N1/4+y)>N 所以是必不可能的 所以如果M对答案有贡献,它必然是完全立方数!

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,int> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const int bb=18;
const double eps=1e-10;
const double pi=acos(-1);
LL n,m;
bool isprime[N];
int prime[N];
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{

    FOR(i,2,1e5){
        if(isprime[i]==0){
            prime[++m]=i;
            for(int j=2;1ll*i*j<=1e5;j++){
                isprime[i*j]=1;
            }
        }
    }
    int t; r(t);
    while(t--){
        r(n);
        LL l=1,r=4e4,nn;
        LL ans=1;
        while(l<=r){
            LL mid=(l+r)>>1;
            if(mid*mid*mid*mid>n){
                nn=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        FOR(i,1,m){
            int t=prime[i];
            if(t>nn||t>n) break;
            int cnt=0;
            while(n%t==0){
                cnt++;
                n/=t;
                if(cnt==3){
                    cnt=0;
                    ans*=t;
                }
            }
        }
        //cout<<ans<<endl;
        l=1,r=1e6+2;
        LL res=0;
        while(l<=r){
            LL mid=(l+r)>>1;
            if(mid*mid*mid<=n){
                res=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        //cout<<res<<endl;
        if(res*res*res==n){
            ans*=res;
        }
        cout<<ans<<endl;
    }
    return 0;
}


F - 十字阵列

计算每次操作的贡献即可,因为每次操作的每个格子的i+j是很容易算出来的(m*x+n*y-x-y)+(n+1)*n/2+(m+1)*m/2;

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,string> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-10;
const double pi=acos(-1);
int n,m;
int f[N],g[N];
int p[N];
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    int k;
    r(n); r(m); r(k);
    LL ans=0;
    FOR(i,1,k){
        int x,y,z;
        rrr(x,y,z);
        LL dmg=(m*x+n*y-x-y)+(n+1)*n/2+(m+1)*m/2;
        ans=(ans+dmg*z%mod)%mod;
        //cout<<ans<<endl;
    }
    cout<<ans<<endl;
    return 0;
}


G - 括号序列

就是括号匹配…

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,string> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-10;
const double pi=acos(-1);
int n,m;
char str[N];
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    int t;
    r(t);
    while(t--){
        r(n);
        scanf("%s",str+1);
        stack<char> s;
        FOR(i,1,n){
            if(s.size()){
                if(str[i]==')'&&s.top()=='('){
                    s.pop();
                }
                else{
                    s.push(str[i]);
                }
            }
            else{
                s.push(str[i]);
            }
        }
        cout<<s.size()<<endl;
    }
    return 0;
}


H - 云

这题如果考虑两边云的运动,处理会比较复杂,我们考虑他们的相对运动。以第一象限的云为参考系,那么第三象限的云一步往右和上各走一步在这里插入图片描述
黑色为坐标轴 蓝色为矩形 红色为y=-x 绿线为垂直于y=-x的直线 紫色为坐下矩形投影平移出来的线段 深蓝色为右上矩形投影平移出来的线段
不难发现,只要矩形关于y=-x的投影相交即 他们平移会相交
在这里插入图片描述
现在矩形左上顶点为C 那么 O B = x , B C = y OB=x,BC=y OB=x,BC=y,我们要求的是他投影之后的坐标 t t t ,因为 A B = B C AB=BC AB=BC O A = x − y OA=x-y OA=xy 那么 2 t = O A \sqrt{2}t=OA 2 t=OA 但是这个根号完全不需要,因为所有的坐标都带根号 和都不带最终并不影响结果啊,我们去掉无妨
后面的统计排个序就好找了~

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,int> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const int bb=18;
const double eps=1e-10;
const double pi=acos(-1);
LL n,m;
struct node
{
    int x,type,lr;
    node(){};
    node(int xx,int tt,int ll){
        x=xx; type=tt; lr=ll;
    }
    bool operator< (node b)
    {
        if(x==b.x) return lr>b.lr;
        return x<b.x;
    }
}f[N];
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    r(n); r(m);
    int cnt=0;
    FOR(i,1,n){
        int a,b,c,d;
        rrr(a,b,c); r(d);
        f[++cnt]=node(a-b,1,1);
        f[++cnt]=node(c-d,1,-1);
    }
    FOR(i,1,m){
        int a,b,c,d;
        rrr(a,b,c); r(d);
        f[++cnt]=node(a-b,0,1);
        f[++cnt]=node(c-d,0,-1);
    }
    sort(f+1,f+cnt+1);
    int c1=0,c2=0;
    LL ans=0;
    FOR(i,1,cnt){
        if(f[i].type) c1+=f[i].lr;
        else c2+=f[i].lr;
        if(f[i].lr==1){
            if(f[i].type) ans+=c2;
            else ans+=c1;
        }
    }
    cout<<ans<<endl;
    return 0;
}


I - 导航系统

这题考察的应该是 最小生成树+最短路+并查集 出题人用心啦~
很容易知道的是在一个树上最短的莫过于两点之间的边,其他任意的边都是这些小边的和,所以我们构造最小生成树,跑一遍最短路得到各点之间的距离,再看大边之间是否符合即可(其中最小生成树用到了并查集)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,int> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const int bb=18;
const double eps=1e-10;
const double pi=acos(-1);
LL n,m;
int dad[M];
LL dis[M][M];
int in[M][M];
int rt[M][M];
struct node
{
    int x,y,v;
    node(){};
    node(int xx,int yy,int vv)
    {
        x=xx; y=yy; v=vv;
    }
    friend bool operator< (node a,node b)
    {
        return a.v>b.v;
    }
};
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int seek(int k)
{
    return k==dad[k]?k:dad[k]=seek(dad[k]);
}
int main()
{
    r(n);
    priority_queue<node> q;
    priority_queue<int,vector<int> ,greater<> > ans;
    FOR(i,1,n){
        FOR(j,1,n){
            r(in[i][j]);
            if(i!=j){
                rt[i][j]=1e9+7;
                q.push(node(i,j,in[i][j]));
            }
        }
        dad[i]=i;
    }
    int cnt=0;
    while(q.size()&&cnt<n-1){
        node now=q.top();
        q.pop();
        //cout<<now.v<<endl;
        int x=seek(now.x);
        int y=seek(now.y);
        if(x!=y){
            cnt++;
            dad[x]=y;
            rt[now.x][now.y]=now.v;
            rt[now.y][now.x]=now.v;
            ans.push(now.v);
        }
    }
    FOR(k,1,n){
        FOR(i,1,n){
            FOR(j,1,n){
                if(i!=j&&j!=k&&i!=k) rt[i][j]=min(rt[i][k]+rt[k][j],rt[i][j]);
            }
        }
    }
    bool flag=1;
    FOR(i,1,n){
        FOR(j,1,n){
            if(rt[i][j]!=in[i][j]){
                flag=0;
                break;
            }
        }
    }
    if(flag){
        cout<<"Yes\n";
        while(ans.size()){
            cout<<ans.top()<<endl;
            ans.pop();
        }
    }
    else cout<<"No\n";
    return 0;
}


J - 签到题

设三个圆的半径分别为a b c,那三个边长即为a+b,b+c,a+c
a + b + c = ( x + y + z ) / 2 a+b+c=(x+y+z)/2 a+b+c=(x+y+z)/2(x,y,z为输入的边长)如果能构成三角形,那么很容易知道一边小于两边之和,设 x = a + b , y = a + c , z = b + c 易 得 c = ( x + y + z ) / 2 − x , b = ( x + y + z ) / 2 − z , a = ( x + y + z ) / 2 − y x=a+b,y=a+c,z=b+c 易得c=(x+y+z)/2-x,b=(x+y+z)/2-z,a=(x+y+z)/2-y x=a+b,y=a+c,z=b+cc=(x+y+z)/2x,b=(x+y+z)/2z,a=(x+y+z)/2y其中a b c一定都大于0 故一定存在!

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long LL;
typedef pair<int,string> pt;
const int N=1e6+5;
const int M=2e3+5;
const int INF=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-10;
const double pi=acos(-1);
int n,m;
template<class T>
inline void read(T &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    int a,b,c;
    rrr(a,b,c);
    double tot=(a+b+c)*1.0/2;
    if(a+b>c&&a+c>b&&b+c>a){
        if(tot>a&&tot>b&&tot>c){
            cout<<"Yes\n";
            double f[4];
            f[1]=tot-a;
            f[2]=tot-b;
            f[3]=tot-c;
            sort(f+1,f+4);
            printf("%.2f %.2f %.2f\n",f[1],f[2],f[3]);
        }
        else{
            cout<<"No\n";
        }
    }
    else{
        cout<<"wtnl\n";
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值