【AtCoder Beginner Contest 181】A~F題解


越學越菜系列

於2020.11.2,我綠了(錯亂)

A - Heavy Rotation

簽到題,奇數Black,偶數White。

code:
#include<bits/stdc++.h>
#define N 10000005
#define LL long long 
using namespace std;
 
int t;
int a,b;
 
inline LL qr()
{
	LL x=0,w=1;char a=0;
	while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a^48);a=getchar();}
	return x*w;
}
 
int main()
{
	t=qr();
	if(t%2==1)
	      cout<<"Black"<<endl;
	else
	      cout<<"White"<<endl;
	return 0;
}

B - Trapezoid Sum

依舊是簽到題,對於輸入每一對數(a,b)求以a為首項,公差為1的等差數列和即可。

code:
#include<bits/stdc++.h>
#define N 1000005
#define LL long long 
using namespace std;
 
int n;
LL a[N],b[N];
 
inline LL qr()
{
	LL x=0,w=1;char a=0;
	while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a^48);a=getchar();}
	return x*w;
}
 
LL ans;
 
int main()
{
	n=qr();
	for(register int i=1;i<=n;i++)
		a[i]=qr(),b[i]=qr();
	for(register int i=1;i<=n;i++)
		ans+=(b[i]+a[i])*(b[i]-a[i]+1)/2;
	cout<<ans<<endl;
	return 0;
}

C - Collinearity

給你N∈[3,100]個點,判斷其中是否有三個點在同一條直線上。

做法:

暴力枚舉所有三元組(i,j,k),根據初中知識,先計算出i,j所在直線的表達式在把k的x軸坐標
代入表達式判斷k的y軸坐標與計算結果是否相同即可,注意考慮斜率不存在的情況。

code:
#include<bits/stdc++.h>
#define N 1000005
#define LL long long 
using namespace std;

int n;
int x[122],y[122];

inline LL qr()
{
	LL x=0,w=1;char a=0;
	while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a^48);a=getchar();}
	return x*w;
}

LL ans;
int flag=0;

int main()
{
	n=qr();
	for(register int i=1;i<=n;i++)
		x[i]=qr(),y[i]=qr();
	for(register int i=1;i<=n;i++)//枚舉所有三元組
		for(register int j=1;j<=n;j++)
			for(register int k=1;k<=n;k++)
			{
				if(i==j||k==j||i==k)
					continue;
				if(x[i]==x[j])//考慮斜率不存在
				{
					if(x[j]==x[k])
						flag=1;
					continue;
				}
				if(x[k]==x[j])
				{
					if(x[j]==x[i])
					      flag=1;
					continue;
				}
				if(x[k]==x[i])
				{
					if(x[j]==x[i])
						flag=1;
					continue;
				}
                                //y=kx+b;
				double kl=(double)(y[j]-y[i])/(double)(x[j]-x[i]);//計算斜率,表達式中的k
				double kb=y[i]-x[i]*kl;//表達式中的b
				double ky=(double)x[k]*kl+kb;//將x代入表達式
				double yyy=y[k];
				if(yyy-0.00000001<=ky&&ky<=yyy+0.00000001)//浮點數比較
				{
					//cout<<i<<' '<<j<<' '<<k<<endl;
					flag=1;
					break;
				}
			}
	if(flag)
		cout<<"Yes"<<endl;
	else
		cout<<"No"<<endl;
	return 0;
}

D - Hachi

輸入一個字符串,每個字符為1~9中一個數字
判斷是否能通過重新排列字符串,使得字符串代表的數字為8的倍數。

做法:

暴力枚舉
猜測8的所有倍數滿足一定性質(類比3的倍數各項和也為3的倍數)
通過對一定范圍內8的倍數打表,結果沒找到性質QWQ
最后通過百度,發現所有8的倍數,滿足后三位也為8的倍數(度娘吼啊!)。
那么即可先統計字符串內所有數字的數量,再枚舉1~999所有8的倍數,統計這一個數各個位數數字的數量,
與之前統計的字符串內所有數字的數量比較判斷即可。
要注意字符串長度≤2的情況,特判一下。

code:
#include<bits/stdc++.h>
#define N 1000005
#define LL long long 
using namespace std;
 
char a[200005];
 
inline LL qr()
{
	LL x=0,w=1;char a=0;
	while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a^48);a=getchar();}
	return x*w;
}
 
LL ans;
int flag=0;
int cnt[111];
int cn[11]={};
 
int main()
{
	cin>>(a+1);
	int n=strlen(a+1);
	for(register int i=1;i<=n;i++)//統計字符串內所有數字的數量
		cnt[a[i]^48]++;
	for(register int i=1;i*8<1000;i++)
	{
		int op=i*8;//枚舉1~999所有8的倍數
		for(register int j=0;j<=9;j++)//清空上一次枚舉的情況
			cn[j]=0;
		int c=0;
		while(op)//統計op的位數及所有位數字數量
		{
			c++;
			cn[op%10]++;
			op/=10;
		}
		if(n>3&&c<3)//特判
			continue;
		if(n==2&&c!=2)
			continue;
		if(n==1&&c!=1)
			continue;
		int ko=1;
		for(register int j=0;j<=9;j++)//比較判斷
			if(cnt[j]<cn[j])
				ko=0;
		if(ko==1)
		{
			//cout<<i*8<<endl;
			cout<<"Yes"<<endl;
			return 0;
		}
	}
	cout<<"No"<<endl;
	return 0;
}

E - Transformable Teacher

有一個含有n個數的數列a,和一個含有m個數的數列b。在b中選擇一個數插入a中,並排列a,使得下面的式子最小。

做法:
推論:要使該式子最小,a序列應滿足單調性。

可得到以下做法:
將原a數組排序,對於b中每一個數插入到與其值相近的a中計算答案。
時間復雜度為O(nm)。
考慮優化:

  • 維護a數組差分前綴和可避免大量重復計算。
  • 可以發現在枚舉判斷a時,a存在單調性,可通過二分查找較快的找到相應位置。
    時間復雜度可優化到O((m+n)logn)。
  • 也可以先將b數組排序,建立雙指針,順序枚舉所有的a數組,當滿足b[i]的值與a[i]相鄰時,
    計算該情況答案,最后統計答案最小值即可。
    復雜度O(nlogn+mlogm)。
下面給出該做法的代碼(因為我比較懶,代碼中可能有部分冗余,見諒):
#include<bits/stdc++.h>
#define N 1000005
#define LL long long 
using namespace std;

int n,m;
LL a[200005],b[200005];
LL cha[200005],sum[200004];

inline int qr()
{
	int x=0,w=1;char a=0;
	while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a^48);a=getchar();}
	return x*w;
}

LL ans=0x3f3f3f3f3f3f3f3f;

int main()
{
	n=qr();
	m=qr();
	for(register int i=1;i<=n;i++)
		a[i]=qr();
	for(register int j=1;j<=m;j++)
		b[j]=qr();
	sort(a+1,a+n+1);
	sort(b+1,b+m+1);//對a,b排序
	for(register int i=2;i<=n;i++)
		cha[i]=a[i]-a[i-1];
	for(register int i=1;i<=n;i++)
		if(i>=2)//sum[i]為以i為結尾,從后往前兩兩作查的前綴和
			sum[i]=sum[i-2]+cha[i];//維護前綴和,至於為什么是i-2,顯然QAQ			
	int tot=1;//建立數組b的指針(假的)
	for(register int i=1;i<=n;i++)
	{
		if(tot>m)
			break;
		if(i==1)//特判i==1
			while(b[tot]<=a[i]&&tot<=m)
			{
				ans=min(ans,a[i]-b[tot]+sum[n]-sum[1]);
				tot++;
			}
		if(i==n)//特判i==n
			while(b[tot]>=a[i]&&tot<=m)
			{
				ans=min(ans,sum[i-1]+b[tot]-a[i]);
				tot++;
			}
		while((b[tot]>=a[i]&&b[tot]<=a[i+1])&&tot<=m)
		{
			if(i%2==0)
				ans=min(ans,sum[i]+abs(a[i+1]-b[tot])+sum[n]-sum[i+1]);
			else
				ans=min(ans,sum[i-1]+abs(b[tot]-a[i])+sum[n]-sum[i]);
			tot++;
		}
	}
	cout<<ans<<endl;
	return 0;
}

F - Silver Woods

設y=100,y=-100為平面直角坐標系上下界,在坐標系中有n(1≤n≤100)個釘子,現要使半徑為r的氣球從坐標系左端移動到右端,並使得氣球不超過上下界且不能碰到釘子,求氣球的最大半徑,精度為(10^(-4))。

做法:
  • 首先,考慮二分答案,答案上下界分別為0和100顯然
  • 其次,有一個十分巧妙的做法,將每一個釘子看做半徑為r的圓,將氣球看做點,氣球路徑上下界分別看做100-r和-100+r。
  • 那么,則釘子所代表的圓相交的話則代表“此路不通”,那么如果存在一系列相交的圓橫貫上下界,則該r無法成立(妙哇Orz)。

綜上:用二分答案的方式枚舉可能的r,用並查集維護相交的圓以及集合的最高點和最低點,最后對於每個集合判斷即可。

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

int n,fa[300];
double dis[300][300];
double l=0.0,r=100.0;
const double inf=0.000000000001;
double x[300],y[300],hig[300],dep[300];

inline int qr()
{
	char a=0;int x=0,w=1;
	while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a^48);a=getchar();}
	return x*w;
}

inline int Find(int x)//並查集路徑壓縮
{
	int t1=x,t2;
	while(fa[t1]!=t1)
		t1=fa[t1];
	while(fa[x]!=x)
	{
		t2=fa[x];
		fa[x]=t1;
		x=t2;
	}
	return x;
}

int main()
{
	n=qr();
	for(register int i=1;i<=n;i++)
		x[i]=qr(),y[i]=qr();
	for(register int i=1;i<=n;i++)
		for(register int j=1;j<=n;j++)
			dis[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));//預處理出任意兩點間距離
	while(l+inf<r)
	{
		double mid=(l+r)/2;
		for(register int i=1;i<=n;i++)
		{
			hig[i]=y[i]+mid;//處理集合中最高點
			dep[i]=y[i]-mid;//處理集合中最低點
			fa[i]=i;//預處理並查集
		}
		int flag=1;
		for(register int i=1;i<=n;i++)//
			for(register int j=1;j<=n;j++)
			{
				if(i==j)
					continue;
				if(dis[i][j]<(mid*2))
				{
					int fi=Find(i);
					int fj=Find(j);
					fa[fj]=fi;//並查集合並
					hig[fi]=max(hig[fi],hig[fj]);//更新並查集最高點
					dep[fi]=min(dep[fi],dep[fj]);//更新並查集最低點
				}
			}
		for(register int i=1;i<=n;i++)//枚舉每一個並查集如果有一個集合將道路全部封死則不可通過
			if(fa[i]==i)
				if(hig[i]+mid>100.0000&&dep[i]-mid<-100.0000)
					flag=0;
		if(flag)
			l=mid+inf;
		else
			r=mid;
	}
	printf("%.10f\n",l);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM