忍者钩爪 ( ninja) 题解———2019.10.19
原题链接:https://www.luogu.org/problem/T104248
【问题 描述 】
小 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% 的数据来说 可以直接暴力记忆化搜索得到答案,即维护每一点的坐标是否可以到达
菜鸡代码如下:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
int n,m,f[10001];
bool b[200001],vis[200001];
void awa(int k,int c) {
if(abs(k)>10000)return;
for(int i=1; i<=n; i++) {
int t=f[i]*2-k;
if(b[t+10000])continue;
b[t+10000]=true;
c++;
awa(t,c);
c--;
}
}
int main() {
std::cin>>n>>m;
for(int i=1; i<=n; i++) cin>>f[i];
awa(0,1);
for(int i=1; i<=m; i++) {
int x;
std::cin>>x;
if(b[x+10000])std::cout<<"Yes\n";
else std::cout<<"No\n";
}
return 0;
}
对于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
即可,可获得满分。
附赠std:
#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("%lld",&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("%lld",&q);
if(Set.find((q%GCD+GCD)%GCD)!=Set.end())
puts("Yes");
else
puts("No");
}
// fclose(stdin);
// fclose(stdout);
return 0;
}