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
",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
",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
",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 o min(x,y))
- (x,y o gcd(x,y))
考虑画出决策树,我们发现 (min(x,y)) 操作相当于把较小的数删除。
有 B:
- (x,y o delete : y,xle y)
- (x,y o gcd(x,y))
想要删除 (y) ,用 (x) 删和用 (a_{min}) 删是一样了,不妨规定都用 (a_{min}) 删(简化了)。
有 C:
- (y o delete : y, y ge a_{min})
- (x,y o 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) 的约数。
故最多有 (nsqrt{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
",ans);
return 0;
}
参考文章
- 【koko的比赛杂谈】AtCoder Beginner Contest 191
- #AtCoder Beginner Contest 191 Editorial
- AtCoder Beginner Contest 191 题解 by