题目名称 |
忍者钩爪 |
|
|
程序文件名 |
ninja |
|
|
输入文件名 |
ninja.in |
|
|
输出文件名 |
ninja.out |
|
|
每个测试点时限 |
1秒 |
|
|
内存限制 |
256MB |
|
|
测试点数目 |
10 |
|
|
每个测试点分值 |
10 |
|
|
是否有部分分 |
无 |
|
|
试题类型 |
传统 |
|
|
提交源程序文件名 |
|||
对于Pascal语言 |
ninja.pas |
|
|
对于C语言 |
ninja.c |
|
|
对于C++语言 |
ninja.cpp |
|
|
编译选项 |
|||
对于C++ 语言 |
-lm |
|
|
对于C 语言 |
-lm |
|
|
对于Pascal 语言 |
|
|
|
注意事项: 1. 文件名(程序名和输入输出文件名)必须使用英文小写。 2. 除非特殊说明,结果比较方式均为忽略行末空格及文末回车的全文比较。 3. C/C++中函数main()的返回值类型必须是int,程序正常结束时的返回值必须 是0。 4. 全国统一评测时采用的机器配置为:CPU AMD Athlon(tm) II x2 240 processor, 2.8GHz,内存4G,上述时限以此配置为准。 5. 只提供Linux格式附加样例文件。 6. 评测在NOI Linux下进行。 7. 编译时不打开任何优化选项。 |
题目名称 |
忍者钩爪 |
|
|
程序文件名 |
ninja |
|
|
输入文件名 |
ninja.in |
|
|
输出文件名 |
ninja.out |
|
|
每个测试点时限 |
1秒 |
|
|
内存限制 |
256MB |
|
|
测试点数目 |
10 |
|
|
每个测试点分值 |
10 |
|
|
是否有部分分 |
无 |
|
|
试题类型 |
传统 |
|
|
提交源程序文件名 |
|||
对于Pascal语言 |
ninja.pas |
|
|
对于C语言 |
ninja.c |
|
|
对于C++语言 |
ninja.cpp |
|
|
编译选项 |
|||
对于C++ 语言 |
-lm |
|
|
对于C 语言 |
-lm |
|
|
对于Pascal 语言 |
|
|
|
注意事项: 1. 文件名(程序名和输入输出文件名)必须使用英文小写。 2. 除非特殊说明,结果比较方式均为忽略行末空格及文末回车的全文比较。 3. C/C++中函数main()的返回值类型必须是int,程序正常结束时的返回值必须 是0。 4. 全国统一评测时采用的机器配置为:CPU AMD Athlon(tm) II x2 240 processor, 2.8GHz,内存4G,上述时限以此配置为准。 5. 只提供Linux格式附加样例文件。 6. 评测在NOI Linux下进行。 7. 编译时不打开任何优化选项。 |
忍者钩爪
(ninja.pas/c/cpp)
【问题描述】
小Q是一名酷爱钩爪的忍者,最喜欢飞檐走壁的感觉,有一天小Q发现一个练习使用钩爪的好地方,决定在这里大显身手。
场景的天花板可以被描述为一个无穷长的数轴,初始小Q挂在原点上。数轴上有N个坐标为整数的圆环供小Q实现钩爪移动。具体操作为:小Q可以将钩爪挂到圆环上,进而荡到关于圆环坐标轴对称的位置。例如小Q在3,圆环在7,则小Q可以通过该圆环移动到11。
现在一个问题难倒了小Q,如何判断自己能否到达某个整点呢?
【输入格式】
第一行两个整数N,M,表示圆环的数量和询问组数
接下来一行共N个整数描述每个圆环的坐标(可重复)
接下来M行每行包含一个整数描述询问
【输出格式】
共M行对应M个询问,若小Q能移动到目标点,输出Yes,否则输出No
【样例输入】
2 2
1 3
3
4
【样例输出】
No
Yes
【数据范围和注释】
对于30%的数据,M≤N≤10,输入坐标绝对值均小于1000。
对于60%的数据,M≤N≤5000。
对于100%的数据,M≤N≤100000,输入坐标绝对值均小于10^18。
思路:
对于30%的分数
可以使用暴力记忆化搜索得出答案。即维护每个坐标是否可达,继而进行搜索。
对于60%的分数
通过观察可知设当前坐标为x,则通过坐标为a的圆环可移动到2a-x处。连续通过两个圆环(a,b)可以移动到x+(2b-2a)处。
先以移动步数为偶数情况考虑简化版问题:设圆环坐标为a[1]~a[n],对于任意两个圆环,可由坐标x变为x+2(a[j]-a[i]),题目转化为对于N^2个数其中b[i,j]=2(a[j]-a[i]),通过有限次加减运算能否由x=0变化至目标。
根据广义裴蜀定理以及扩展欧几里得相关原理可知,当且仅当目标为gcd的倍数时有解。故预处理出全部可能的2(a[j]-a[i]),求出其最大公约数,在判断目标是否为gcd的倍数即可。
对于奇数的情况,可以通过枚举第一步的方案转化为偶数的情况,即维护一个set表示0步或1步可达点集(mod gcd意义下),再查询目标点在mod gcd下是否属于这个集合即可。复杂度瓶颈在于N^2个数求gcd。
对于100%的分数
通过欧几里得算法的性质与更相减损术可知gcd(a,b)=gcd(a-b,b)。设p1={2*(a[i]-a[1])|i>1}的最大公约数,设p2={2*(a[i]-a[j])}的最大公约数,易知p1>=p2(因为p1比p2约束宽松)。而对于任意i,j由于p1同时是2*(a[i]-a[1])、2*(a[j]-a[1])的约束,那么p1也一定是任意2*(a[i]-a[1])-2*(a[j]-a[1])=2*(a[i]-a[j])的约数,故p1<=p2。综上所述p1=p2,这样就不需要N^2个数同时求gcd了,只求p1即可,可获得满分。
#include<map> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 100010 using namespace std; map<int,bool>ma; int n,m,num=1; int pos[MAXN],p[MAXN*100]; int main(){ freopen("ninja.in","r",stdin); freopen("ninja.out","w",stdout); scanf("%d%d",&n,&m); p[num]=0; ma[0]=true; for(int i=1;i<=n;i++){ scanf("%d",&pos[i]); ma[pos[i]*2]=true; p[++num]=pos[i]*2; } for(int i=1;i<=20;i++){ int nu=num; for(int j=1;j<=nu;j++) for(int k=1;k<=n;k++){ int t=2*pos[k]-p[j]; if(!ma[2*pos[k]-p[j]]){ ma[2*pos[k]-p[j]]=true; p[++num]=2*pos[k]-p[j]; } } } for(int i=1;i<=m;i++){ int opt; scanf("%d",&opt); if(ma[opt]) cout<<"Yes"<<endl; else cout<<"No"<<endl; } }
#include <iostream> #include <cstdio> #include <set> using namespace std; const int N=200010; int n,m; long long a[N],GCD=0; set<long long> Set; long long gcd(long long a,long long b) { return b?gcd(b,a%b):a; } long long qabs(long long x){return x<0?-x:x;} int main() { freopen("ninja.in","r",stdin); freopen("ninja.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%I64d",&a[i]); for(int i=2;i<=n;i++) GCD=gcd(2LL*qabs(a[i]-a[1]),GCD); if(GCD==0)GCD=1000000000LL*1000000000LL; Set.insert(0LL); for(int i=1;i<=n;i++) Set.insert(((2*a[i])%GCD+GCD)%GCD); while(m--) { long long q; scanf("%I64d",&q); if(Set.find((q%GCD+GCD)%GCD)!=Set.end()) puts("Yes"); else puts("No"); } fclose(stdin); fclose(stdout); return 0; }
思路:见博客
http://www.cnblogs.com/cangT-Tlan/p/7677709.html
思路:不会。