[ABC191] AtCoder Beginner Contest 191

AtCoder Beginner Contest 191

Tasks


Task Name Time Limit Memory Limit
A Vanishing Pitch 2 sec 1024 MB Submit
B Remove It 2 sec 1024 MB Submit
C Digital Graffiti 2 sec 1024 MB Submit
D Circle Lattice Points 2 sec 1024 MB Submit
E Come Back Quickly 3 sec 1024 MB Submit
F GCD or MIN 2 sec 1024 MB Submit

A Vanishing Pitch

小学数学题。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

int main()
{
//	freopen("1.in","r",stdin);
	int v,t,s,d;
	cin>>v>>t>>s>>d;
	if(d<t*v || d>s*v) puts("Yes");
	else puts("No");
	return 0;
}

B Remove It

签到题。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

int n,m;

int main()
{
//	freopen("1.in","r",stdin);
	int x;
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) {
		scanf("%d",&x);
		if(x!=m) printf("%d ",x);
	}
	puts("");
	return 0;
}

C Digital Graffiti

题意:给定一个网格图上的多边形,求这个多边形有多少条边。

Sample Input 1

5 5
.....
.###.
.###.
.###.
.....

Sample Output 1

4

模拟题。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

const int N=200+5;
const int dx[]={0,0,1,-1},dy[]={1,-1,0,0};

int n,m;
char a[N][N];
bool st[N][N][4]; 

int main()
{
//	freopen("1.in","r",stdin);
	int i,j,k;
	int x,y;
	
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) 
		scanf("%s",a[i]+1);
	
	int ans=0;
	for(i=2;i<n;i++) 
		for(j=2;j<m;j++) {
			for(k=0;k<4;k++) {
				if(a[i][j]=='.') continue;
				
				x=i+dx[k]; y=j+dy[k];
				if(a[x][y]!='.') continue;
				if(k<2) st[i][j][k]=st[i-1][j][k];
				else if(k>=2) st[i][j][k]=st[i][j-1][k];
				
				if(!st[i][j][k])
					ans++,st[i][j][k]=true;//,printf("%d %d %d\n",i,j,k);
			}	
		}
	
	cout<<ans<<endl;
	return 0;
}

D Circle Lattice Points

题意:给定一个圆,求圆内或圆上的整点数量。\(0<R≤10^5\)

全场最毒瘤的一道题,卡精度差评。

思路:肯定是枚举一维,\(\mathcal O(1)\) 计算另一维。

然而交上去 WA 了无数次,心态爆炸。

WA 的原因大概是有一个点恰恰好好在圆上,然后 long double= 又不准。

卡精度方法

  • long double
  • 先把 半径 \(r\) 加上一个很小的值,这样就不会有点在圆上,计算圆内的点就可以了。

貌似 cmath 里的 floorceil 精度还挺高的,就不用换了。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

#define double long double
const double eps=1e-14;

double a,b,r;

int main()
{
//	freopen("1.in","r",stdin);

	cin>>a>>b>>r;
	
	LL ans=0;
	r+=eps;
	for(int x=a-r-1;x<=a+r+1;x++) {
		if(r*r<(x-a)*(x-a)) continue; 
		double yu=sqrt(r*r-(x-a)*(x-a))+b;
		double yd=-sqrt(r*r-(x-a)*(x-a))+b;
	
		ans+=floor(yu)-ceil(yd)+1;
	}
	printf("%lld\n",ans);
	return 0;
}

E Come Back Quickly

题意:求每个点所在的最小环。

\(0 \le n,m \le 2000\)

经典问题:求最短路时,先用 \(x\) 松弛一遍与 \(x\) 相邻的点,但不把 \(x\) 入队,把与 \(x\) 相邻的点入队。然后到 \(x\) 的最短路就是答案。

自证不难

Code:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

const int N=2e3+5,INF=0x3f3f3f3f;

int one[N],idx;
int ver[N],edge[N],Next[N];
inline void AddEdge(int a,int b,int c)
{
	Next[idx]=one[a]; ver[idx]=b; edge[idx]=c; one[a]=idx++;
}

int dis[N];
bool vis[N];

void spfa(int st)
{
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	queue<int> q;
	
	int i;
	int x,y,z;
	
	for(i=one[st];~i;i=Next[i]) {
		y=ver[i]; z=edge[i];
		if(dis[y]>z) {
			dis[y]=z;
			if(!vis[y]) 
				q.push(y),vis[y]=true;
		}
	}
	
	while(q.size()) {
		x=q.front(); q.pop();
		vis[x]=false;
		for(i=one[x];~i;i=Next[i]) {
			y=ver[i]; z=edge[i];
			if(dis[y]>dis[x]+z) {
				dis[y]=dis[x]+z;
				if(!vis[y]) 
					q.push(y),vis[y]=true;
			}
		} 
	}
}

int n,m;

int main()
{
//	freopen("1.in","r",stdin);
	int i;
	int x,y,z;
	
	scanf("%d%d",&n,&m);
	memset(one,-1,sizeof one);
	for(i=1;i<=m;i++) {
		scanf("%d%d%d",&x,&y,&z);
		AddEdge(x,y,z);
	}
	for(i=1;i<=n;i++) {
		spfa(i);
		if(dis[i]==INF) puts("-1");
		else printf("%d\n",dis[i]); 
	}
	return 0;
}

F GCD or MIN

非常有趣的一道思维题。

Problem Statement

There are N integers A1,A2,A3,…,AN written on a blackboard.
You will do the following operation N−1 times:

  • Choose two numbers written on the blackboard and erase them. Let \(x\) and \(y\) be the erased numbers. Then, write \(\gcd(x,y)\) or \(\min(x,y)\) on the blackboard.

After N−1 operations, just one integer will remain on the blackboard. How many possible values of this number are there?

Constraints

  • \(2≤N≤2000\)
  • \(1≤Ai≤10^9\)
  • All values in input are integers.

思路:

简化模型。

原问题A:

  • \(x,y \to \min(x,y)\)
  • \(x,y \to \gcd(x,y)\)

考虑画出决策树,我们发现 \(\min(x,y)\) 操作相当于把较小的数删除。

有 B:

  • \(x,y \to delete \: y,x\le y\)
  • \(x,y \to \gcd(x,y)\)

想要删除 \(y\) ,用 \(x\) 删和用 \(a_{min}\) 删是一样了,不妨规定都用 \(a_{min}\) 删(简化了)。

有 C:

  • \(y \to delete \: y, y \ge a_{min}\)
  • \(x,y \to \gcd(x,y)\)

现在的操作流程:取一些数的 \(\gcd\) ,用数列中最小的数删数,取一些数的 \(\gcd\) ,用数列中最小的数删数,取一些数的 \(\gcd\) ,用数列中最小的数删数,\(\cdots\)

考虑 \(a_{min}\) 的取值,可能是原来的 \(a_{min}\) 也可能是新生成的 \(\gcd\) .

因为 \(\gcd(x,y)\le \min(x,y)\) ,所以数列中的数如果现在删 删的掉,以后删也都删得掉 。

不妨都留在后面删。

有一个考虑的细节:删数时删掉 生成的 \(\gcd\) 和删掉 原序列里生成 该 \(\gcd\)\(a_i\) 们是等价的,因此我们可以只删除原序列的数。

现在的操作流程:取一些数的 \(\gcd\) ,用数列中最小的数删数,删光,只剩下之前生成的一个 \(\gcd\)

删光的条件:(设 \(a'_{min}\) 为原序列的最小值)

  • 倘若 \(a'_{min}\) 在序列中,只需 \(\gcd \le a'_{min}\)
  • 倘若 \(a'_{min}\)\(\gcd\) 中,那么已经有 \(\gcd \le a'_{min}\)

故只需 \(\gcd \le a'_{min}\)

故原问题转化为

\[\sum_{S \in A} [\gcd(S) \le a'_{min}] \]

由于 \(\gcd\) 一定是某个 \(a_i\) 的约数。

故最多有 \(n\sqrt{A_{max}}\) 个。

一个一个拿出来 check 即可。

check(d) :判断能不能生成 \(d\)

  • 对于 不是 \(d\) 的倍数的 \(a_i\) ,肯定不能放进来。
  • 对于 \(d|a_i\) ,放进 \(a_i\) 只会使 答案变小,不会变大。

因此把 \(d|a_i\)\(a_i\) 求一遍 \(\gcd\) ,看看是否满足即可。

实现时还有一点技巧。

Code:

#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

int gcd(int a,int b)
{
	if(b==0) return a;
	else return gcd(b,a%b);
}

const int N=2048;

map<int,int> mp;
int n;
int a[N];

int main()
{
//	freopen("1.in","r",stdin);
	int i,j;
	
	scanf("%d",&n);
	for(i=1;i<=n;i++) 
		scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	for(i=1;i<=n;i++) {
		for(j=1;j*j<=a[i];j++) {
			if(a[i]%j==0) {
				if(j<=a[1]) mp[j]=gcd(mp[j],a[i]);
				if(a[i]/j<=a[1]) mp[a[i]/j]=gcd(mp[a[i]/j],a[i]); 
			}
		}
	}
	int ans=0;
	for(map<int,int>::iterator it=mp.begin();it!=mp.end();it++) 
		if(it->first==it->second) ans++;
	printf("%d\n",ans);
	return 0;
}

参考文章

posted @ 2021-03-03 22:15  cjlworld  阅读(271)  评论(0编辑  收藏  举报