“蔚来杯“2022牛客暑期多校训练营1

A题

给出一个发电站和多个建筑物的一维坐标,需要通过电塔和电线将它们全部连接起来,实现所用电线长度最短。在发电站/建筑物半径范围内可以直接与电塔相连,电塔与电塔之间通过电线相连。其实可以很轻松的判断出是一道区间合并题。(虽然和队友讨论了很久......)

以下是本蒟蒻的代码。。。

#include <bits/stdc++.h>
using namespace std;
int n,lm,rm,p,l,ans,kl,kr;
struct op{
   int x,y,l,r;};
op s[200005];  
bool cmp(op a,op b)
{
	if (a.l>b.l)
		return false;
	if (a.l<b.l)
		return true;
	else if (a.r<b.r)
	  return false;
	return true;
 } 
int main()
{
	scanf("%d",&n);
	scanf("%d %d",&s[1].x,&s[1].y);
	s[1].l=s[1].x-s[1].y;
	s[1].r=s[1].x+s[1].y;
	lm=s[1].x;rm=s[1].x;
	for (int i=2;i<=n;i++){
	  	scanf("%d %d",&s[i].x,&s[i].y);
	  	if (s[i].x<lm)
	  		lm=p;
	  	if (s[i].y>rm)
	  	    rm=p;
	  	s[i].l=s[i].x-s[i].y;
		s[i].r=s[i].x+s[i].y;
	  }
	sort (s+1,s+1+n,cmp);
	int dis=0;
	kl=s[1].l;kr=s[1].r;
	for (int i=2;i<=n;i++){
		if (s[i].r>kr)
			kr=s[i].r;
		dis=s[i+1].l-kr;
		if (dis>0)
		  ans+=dis;
	}
	if (kl>lm)
		ans+=kl-lm;
	if (kr<rm)
		ans+=rm-kr;
	cout<<ans;
	return 0;
 } 

但因为自己的粗心,少考虑了左边合并后,右边也得改区间,所以wa了2发,靠队友才过的wwwwww(队友太强了)

下面是队友大佬的代码,更好理解

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<bits/stdc++.h>
#include<algorithm>
#include<cmath>
#include<map>
#include<string>
#include<sstream>
#include<time.h>
using namespace std;

const int N=2e5+10;

struct node{
	long long l,r;
}a[N];

bool cmp(node x,node y){
    if(x.l!=y.l)return x.l<y.l; 
    return x.r>y.r;
}

int main(){
	int n;
	long long xs,rs,ans=0,rr;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>xs>>rs;
		a[i].l=xs-rs;
		a[i].r=xs+rs;
	}
	sort(a+1,a+n+1,cmp);
	rr=a[1].r;
	for(int i=2;i<=n;i++){
		if(rr<a[i].l){
			ans+=a[i].l-rr;
			rr=a[i].r;
		}
		else{
			rr=max(rr,a[i].r);
		}
	}
	cout<<ans;
}

C题

结合图形可以更好理解题目,这里用一下其他大佬((Tan_Yuu))的图。

题目大概就是找出没有被覆盖的区域的点数量,观察图可以发现,被覆盖区域由两点斜线决定,斜率最大的斜线是被覆盖区域的上界,斜率最小的斜线是被覆盖区域的下界。可以通过两次遍历维护每行的好点数;对于每次遍历,我们只需要记录已遍历部分中斜率最大/最小的边界线,由其与此行的交点坐标限定此行的好点数。在维护边界线时,注意边界情况(斜率为0);两次遍历后,累加每行的好点数即可得到答案,每次查询的时间复杂度为O ( n ) 。

因为自己太拉了,比赛的时候没看这道题,队友直接AC了,被带飞了。

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

const int N=200010;

int X[N],T1[N],T2[N]; 

int main(){
	int n,m,k,q,px[N],py[N];
	cin>>n>>m>>k>>q;
	for(int i=1;i<=k;i++){
		cin>>px[i]>>py[i];
	}
	while(q--){
		int id;
		cin>>id;
		cin>>px[id]>>py[id];//更新座位 
		for(int i=1;i<=m;i++)X[i]=n+1;
		for(int i=1;i<=k;i++)X[py[i]]=min(X[py[i]],px[i]);//取每行最左一个已被占的座位 
		for(int i=1,p=0;i<=m;i++){//从下往上扫描,p代表对第行列影响最大的行数 
			if(X[i]!=n+1&&(p==0||(long long)X[i]*(p-1)<(long long)X[p]*(i-1)))p=i;//整形相乘,强制转换为long long型以防爆内存。两式相乘为两式相除所得斜率表达式的变式 
			if(p==0)T1[i]=n;//暂无其他行影响第i行 
			else if(p==1){//p==1时,p-1为0,不能作除数,单独讨论 
				if(i==1)T1[i]=X[i]-1;
				else T1[i]=n;
			}
			else{
				 int tmp=(long long)(i-1)*X[p]/(p-1);//求当前受其他行影响的第i行最右的好座位 
				 if((long long)tmp*(p-1)==(long long)(i-1)*X[p])tmp--;//所求的好座位被本行的人占住 
				 T1[i]=min(tmp,n);//防止超出教室 
			}
		}
		for(int i=m,p=0;i>=1;i--){//同上,从上往下再扫描一遍 
			if(X[i]!=n+1&&(p==0||(long long)X[i]*(p-m)>(long long)X[p]*(i-m)))p=i;
			if(p==0)T2[i]=n;
			else if(p==m){
				if(i==m)T2[i]=X[i]-1;
				else T2[i]=n; 
			}
			else{
				int tmp=(long long)(i-m)*X[p]/(p-m);
				if((long long)tmp*(p-m)==(long long)(i-m)*X[p])tmp--;
				T2[i]=min(tmp,n);
			}
		}
		long long ans=0;
		for(int i=1;i<=m;i++)ans+=min(T1[i],T2[i]);//生成答案 
		printf("%lld\n",ans);
	}
	return 0;
} 

D题

没什么好讲的,就一道平面几何题目,看图像可以直接做出来(不会证明,但凭借高中数学基础可以轻松判断)

 但比赛的时候大家心急了,就疯狂的尝试,然后wa了5发,后面发现这个结论,后悔的要死。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<bits/stdc++.h>
#include<algorithm>
#include<cmath>
#include<map>
#include<string>
#include<sstream>
#include<time.h>
using namespace std;
void solve(){
	long long r,x,y,d;
	double dis,ans,x1,x2,l,xita;
	cin>>r>>x>>y>>d;
	dis=sqrt((double)(x*x+y*y));
	x1=sqrt(double(r*r)-(dis-d)*(dis-d));
	x2=sqrt(double(r*r)-(dis+d)*(dis+d));
	l=sqrt(double(4*d*d)+(x1-x2)*(x1-x2));
	l/=2; 
	xita=asin(l/r);
	ans=xita*2*r;
	printf("%.14lf\n",ans);
}

int main(){
	int t;
	cin>>t;
	while(t--)solve();
	return 0;
} 

G题

单纯签到,但我们还是先wa了两发(5555555~)

#include <iostream>
#include <cstring>
using namespace  std;
#define int long long int
 
int main()
{
    string s;
    cin>>s;
    int f=1;
    int n=s.length();
    if(n<=1)
    {
        cout<<s;
    }
    else
    {
        for(int i=0;i<n-1;i++)
        {
             cout<<"9";
             if(s[i]!='9')
             {
                  f=0;
             }
         }
         if(f==1)
         {
               cout<<s[n-1];    
         }
      }
    return 0;
}

下面是补题...

I题

有一副麻将,初始给你13张牌(每张牌不超过2张),自己摸牌自己打,问预期要打多少个回合才能凑够七对牌。看大佬用概率dp,如果摸到的牌能够凑成一对,就留下,否则就丢掉。令dp[i][j]为当前手牌中有i张单牌,且剩余j张牌没摸时达成七对的数学期望,则有dp方程如下:

\left\{\begin{matrix}1+\frac{j-3}{j} dp[i][j-1](s=1) \\ 1+\frac{3i}{j}dp[i-2][j-1]+\frac{j-3i}{j}dp[i][j-1](s>1) \end{matrix}\right.

 虽然没太看懂,但套用dp方程还是补过了这道题。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;

ll km(ll a,ll b)
{
	ll ans=1;
	a%=mod;
	while(b)
	{
		if(b&1)
			ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

ll dp[20][200];

ll dfs(ll x,ll sz)
{
	if(x<=0||sz<3*x)return 0;
	if(dp[x][sz])return dp[x][sz];

	dp[x][sz]=(1+dfs(x-2,sz-1)*3*x%mod*km(sz,mod-2)%mod+dfs(x,sz-1)*(sz-3*x)%mod*km(sz,mod-2))%mod;
	return dp[x][sz];
}
int tot=1;
void solve()
{

	string s;
	cin>>s;
	map<string,int>mp;
	int cnt=0;
	for(int i=0;i<s.size();i+=2)
	{
		string s1="";
		s1+=s[i];
		s1+=s[i+1];
		mp[s1]++;
		if(mp[s1]==1)cnt++;
		else cnt--;
	}
	cout<<"Case #"<<tot<<": "<<dfs(cnt,123)<<'\n';
    tot++;
}

int main()
{
	for(int i=0;i<=13;i++)
		for(int j=0;j<=126;j++)
			dp[i][j]=0;
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
}

总结:

第一场我们的配合确实不太好,几乎大家都是自己写自己的,没怎么交流。希望之后可以更好一些吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值