2020牛客暑期多校第二场题解

持续填坑中…

A

B

原题链接
做法:根据同弧所对圆周角相等。我们可以枚举一个点A,然后再枚举每个点B,求∠ABO的。将所有的∠ABO存起来,取一个众数。(这里要保证一点就是保证B在AO上方或者下方一个固定的地方,否则不是一个圆

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef __int128_t LLL;
#define N 2000 + 5

int n, ans = 1, X[N], Y[N];

struct Frac//分数
{
	LL fz, fm;//分子,分母
	Frac() : Frac(0, 1){}
	Frac(LL fz, LL fm) : fz(fz), fm(fm) {}
	bool operator < (const Frac &rhs)//重载分数小于
	{
		return (LLL) fz * rhs.fm < (LLL) fm * rhs.fz;
	}
	bool operator == (const Frac &rhs)//交叉相乘等于
	{
		return (LLL) fz * rhs.fm == (LLL) fm * rhs.fz;
	}
}A[N];

int Cross(int lhs, int rhs)//向量差乘
{
	return X[lhs] * Y[rhs] - X[rhs] * Y[lhs];
}

int Dot(int lhs, int rhs)//向量点乘
{
	return X[lhs] * X[rhs] + Y[lhs] * Y[rhs];
}

int Dis2(int lhs, int rhs)//两点距离的平方
{
	int dx = X[lhs] - X[rhs], dy = Y[lhs] - Y[rhs];
	return dx * dx + dy * dy;
}

int Sgn(int x)//角的符号
{
	if (x > 0) return 1;
	if (x < 0) return -1;
	return 0;
}

Frac GetCosAngle2(int i, int j)//求∠ABO
{
	int a2 = Dis2(0, i), b2 = Dis2(i, j), c2 = Dis2(0, j);
	int sgn = Sgn(b2 + c2 - a2);
	return Frac(1LL * sgn * (b2 + c2 - a2) * (b2 + c2 - a2), 4LL * b2 * c2);
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i ++)
		scanf("%d%d", X + i, Y + i);
	for (int i = 1; i <= n; i ++)//枚举每一个点A
	{
		int cnt = 0;
		for (int j = 1; j <= n; j ++)//再枚举每一个点B
			if (Cross(i, j) > 0)
				A[++ cnt] = GetCosAngle2(i, j);
		sort(A + 1, A + cnt + 1);
		for (int l = 1, r; l <= cnt; l = r)//求角度的众数
		{
            r=l;
            while(A[l]==A[r] && r<=cnt){
                r++;
            }
			ans = max(ans, r - l + 1);
		}
	}
	printf("%d\n", ans);
	return 0;
}

C

原题链接
题意:给你一个n个节点的无根树,现在要你找到最少的k条链使得这k条链能覆盖整个树边

题解:
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
struct edge{
    int v,next;
}e[maxn];

struct node{
    int id;
    int dfn;
    bool operator <(const node &rhs) const{
        return dfn<rhs.dfn;
    }
}p[maxn];

int n;
int dfn[maxn];
int head[maxn],cnt=0;
int deg[maxn];
bool vis[maxn];

void add(int u,int v){
    e[++cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}

int ct=0;
void dfs(int x){
    dfn[x]=++ct;
    for (int i=head[x];i;i=e[i].next){
        int v=e[i].v;
        if(!vis[v]){
            vis[v]=1;
            dfs(v);
        }
    }
}

int main(){
    scanf("%d",&n);
    for (int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
        deg[u]++;
        deg[v]++;
    }
    if(n == 1){
        printf("0\n");return 0;
    }
    if(n == 2){
        printf("1\n");return 0;
    }
    for (int i=1;i<=n;i++){
        if(deg[i] != 1){//找到第一个非叶节点,把其作为树根,求一个dfs序
            vis[i] = 1;
            dfs(i);
            break;
        }
    }

    int tot = 0;
    for (int i=1;i<=n;i++){
        if(deg[i] == 1){//选出叶子结点
            p[++tot].id = i;
            p[tot].dfn = dfn[i];
        }
    }
    sort(p+1,p+1+tot);

    printf("%d\n",(tot+1)/2);
    for (int i=1;i<=(tot+1)/2;i++){
        int a=p[i].id,b=p[tot/2+i].id;
        printf("%d %d\n",a,b);
    }
    return 0;
}

D

水题,不解释

#include <bits/stdc++.h>
 
using namespace std;
 
int main(){
     int a1,a2,a3;
     int b1,b2,b3;
     scanf("%d:%d:%d",&a1,&a2,&a3);
     scanf("%d:%d:%d",&b1,&b2,&b3);
     int ans;
     if(b3<a3){
        ans = a3-b3 +(a2-b2)*60+(a1-b1)*3600;
     }
     else{
        ans = b3-a3 +(b2-a2)*60+(b1-a1)*3600;
     }
    if(ans<0)ans = -ans;
     cout<<ans<<endl;
    return 0;
}

E

F

做法:二维单调队列维护区间的最大值,最后求和即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
 
using namespace std;
 
const int N=5005;
 
typedef long long ll;
 
ll gcd(ll a,ll  b){
    return b==0?a:gcd(b,a%b);
}
struct node{
    ll pos,x;
}max[N];
ll h1,t1,h2,t2;
int  a[N][N],w[N][N];
int main(){
    int aa,bb,n;
    scanf("%d%d%d",&aa,&bb,&n);
    int Max = aa>bb?aa:bb;
    for(int i = 1;i<=Max;++i)for(int j = 1;j<=i;++j){
         a[j][i] = a[i][j] = i*j/gcd(i,j);
    }
    for(int j=1;j<=aa;j++){
        h1=h2=1; t1=t2=0;
        for(int i=1;i<=bb;i++){
            if(h1<=t1 && max[h1].pos<i-n+1) h1++;
            while(h1<=t1 && max[t1].x<a[j][i]) t1--;
            max[++t1].pos=i; max[t1].x=a[j][i];
            if(i-n+1>0)
             w[j][i-n+1]=max[h1].x;
        }
    }
    for(int j=1;j<=bb-n+1;j++){
        h1=h2=1; t1=t2=0;
        for(int i=1;i<=aa;i++){
            if(h1<=t1 && max[h1].pos<i-n+1) h1++;
            while(h1<=t1 && max[t1].x<w[i][j]) t1--;
            max[++t1].pos=i; max[t1].x=w[i][j];
            if(i-n+1>0)
             w[i-n+1][j]=max[h1].x;
        }
    }
    ll ans= 0;
    for(int i=1;i<=aa-n+1;i++)
        for(int j=1;j<=bb-n+1;j++)
          {
             ans+=w[i][j];
          }
    printf("%lld\n",ans);
    return 0;
}

G

做法:bitset维护后缀

#include<bits/stdc++.h>
using namespace std;
const int N=4e4+7;
const int NN=2e5+7;
bitset<N> bit[N];
int a[NN],b[N],p[N];
int sum;
bool cmp(const int& x,const int& y){
    return b[x] < b[y];
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
    }
    for(int i=1;i<=m;i++){
        scanf("%d",b+i);
        p[i]=i;
    }
    sort(p+1,p+m+1,cmp);

    sort(b+1,b+m+1);
    for(int i=1;i<=m;i++){
        bit[i] = bit[i-1];
        bit[i].set(m-p[i],1);
    }
    bitset<N> ans;
    for(int i=1;i<m;i++){
        int t=upper_bound(b+1,b+m+1,a[i])-b-1;
        ans >>=1;
        ans.set(m-1,1);
        ans &=bit[t];
    }
    for(int i=m;i<=n;i++){
        int t=upper_bound(b+1,b+m+1,a[i])-b-1;
        ans >>=1;
        ans.set(m-1,1);
        ans &=bit[t];
        sum +=ans[0];
//        cout<<ans[0]<<"*"<<sum<<" ";
    }
    printf("%d\n",sum);
    return 0;
}

H

I

J

做法:轮换与置换

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll n,k;
ll a[maxn],temp[maxn];
ll ans[maxn];
 
int ct=0;
bool vis[maxn];
vector<int>v;
 
void gcd(ll a,ll b,ll &d,ll &x,ll &y) {
    if(!b) { d=a; x=1; y=0;}
    else {gcd(b,a%b,d,y,x); y -= x*(a/b);}
}
ll gcd_inv(ll a,ll n) {
    ll d,x,y;
    gcd(a,n,d,x,y);
    return d == 1 ? (x+n) % n : -1;
}
 
int main(){
    cin>>n>>k;
    for (int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    for (int i=1;i<=n;i++){
        if(!vis[i]){
            v.clear();
            for (int j=i;!vis[j];){
                v.push_back(j);
                vis[j]=1;
                j=a[j];
            }
            ll r = (int)v.size();//环的大小
            ll invk = gcd_inv(k % r,r);
            // cout<<"invk = "<<invk<<endl;
            for (int j=0;j<r;j++){
                temp[j] = v[(j+invk) % r];
            }
            for (int j=0;j<r;j++){
                ans[v[j]] = temp[j];
            }
        }
    }
    for (int i=1;i<=n;i++){
        if(i!=n) printf("%lld ",ans[i]);
        else printf("%lld\n",ans[i]);
    }
    return 0;
}

K

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值