[ABC191] 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 里的 floor 和 ceil 精度还挺高的,就不用换了。
#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}\) 。
故原问题转化为
由于 \(\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;
}
参考文章
- 【koko的比赛杂谈】AtCoder Beginner Contest 191
- #AtCoder Beginner Contest 191 Editorial
- AtCoder Beginner Contest 191 题解 by
 
                    
                     
                    
                 
                    
                

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号