专栏文章
题解:P12541 [APIO2025] Hack!
P12541题解参与者 19已保存评论 21
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 21 条
- 当前快照
- 1 份
- 快照标识符
- @mip8mngu
- 此快照首次捕获于
- 2025/12/03 07:57 3 个月前
- 此快照最后确认于
- 2025/12/03 07:57 3 个月前
首先感谢 milmon 老师提示,我才能摸索出更好的做法。
考场上冲击 T1,毁我大好青春,只得 77 分。
首先先讲一下怎么判定 中有没有 的倍数(note:返回值是否为零等价于序列中数字两两相减,能不能减出 的倍数)
取 ,构造序列 ,根据其返回值是否为零即可判定 中有没有 的倍数。注意这里需要保证 或者 这个构造才是对的,因为要防止 这一段能减出来在 之外的 的倍数导致误判。
update:讲一下问什么这是对的。我们称第一部分为 ,其他的为第二部分。首先第一部分内部没有贡献,第一部分和第二部分互相产生的贡献恰好判断 有没有 的倍数。接下来就是要证明第二部分内部贡献不会影响判断。我们把第二部分想象成一个指针在一个长度为 的环上走, 已经被标记了,走一步将指针往后动 格。如果这个指针走到了被标记的地方就会对返回值产生贡献,或者走到了一个地方两次也会对返回值产生贡献。你发现标记区间长度为 ,所以你在这个环上走一圈,一定会走到这上面,使得返回值不为零,因为如果第二部分内部产生了贡献(走了一圈),一定会使得第一部分和第二部分互相产生贡献,所以这是对的。
取 ,我们先判一下 还是 。
:直接判。
:二分答案,每次判定 中含不含有 的倍数然后动一下边界就行。
以上的做法能做到 分,也是我考场做法。
我们考虑如果 ,那么它一定有一个在 之间的倍数,直接将二分初始边界改为 ,然后你能算出一个 的倍数,枚举其因数然后找出真正的 即可。
这样貌似只能获得 99 分,再加一个剪枝,因为我们已经保证了 ,所以算出来的 的倍数的因数中, 的一定不会是答案,判的时候跳过这些数就行。
CPP#include<bits/stdc++.h>
using namespace std;
#define pb push_back
int hack();
long long collisions(std::vector<long long> x);
#define fo(i,a,b) for(long long i=a;i<=b;i++)
long long get(long long l,long long r)
{
int C=sqrt(r-l+1);
vector<long long> v;
fo(i,1,C) v.pb(i);
l+=C;
while(l<=r) v.pb(l),l+=C;
v.pb(r+1);
return collisions(v);
}
int hack()
{
long long B=sqrt(1e9),v=get(1,B);
if(v)
{
fo(i,1,B) if(collisions({1,i+1})) return i;
return -1;
}
else
{
long long l=5e8,r=1e9;
while(l<r)
{
long long mid=l+r>>1;
if(get(l,mid)) r=mid;
else l=mid+1;
}
vector<long long> v;
for(long long i=1;i*i<=l;i++) if(l%i==0)
{
if(i>B) v.pb(i);
if(i*i!=l&&l/i>B) v.pb(l/i);
}
sort(v.begin(),v.end());
for(auto i:v) if(collisions({1,i+1})) return i;
return -1;
}
}
然后我们尝试结合一下 @yuanruiqi 题解的做法。
询问的时候把每个数乘上 ,这样你就只需要二分奇数即可,也就是你会得到一个 的倍数去除了所有 因子的结果,把它乘上 ,枚举其所有质因数,二分 有几个枚举的质因子因子即可。
以及你其实根本不用判 。为什么呢?
因为长度大于等于 的区间内一定存在 的倍数,所以你的二分区间会一直缩小到至少 (此处的 是初始二分左端点),而在 时,此时已经满足 了,因此接下来再跑这个算法就是对的。
值得注意的是,为了防止询问的数超出 ,我们需要将二分的下界调低一点,我这里是调了 左右。
CPP#include<bits/stdc++.h>
using namespace std;
#define pb push_back
// #include"hack.h"
int hack();
long long collisions(std::vector<long long> x);
#define fo(i,a,b) for(long long i=a;i<=b;i++)
long long get(long long l,long long r)
{
int C=ceil(sqrt(r-l+1));
vector<long long> v;
fo(i,1,C) v.pb(i);
l+=C;
while(l<=r) v.pb(l),l+=C;
v.pb(r+1);
return collisions(v);
}
long long get2(long long l,long long r)
{
int C=sqrt(r-l+1);
vector<long long> v;
fo(i,1,C) v.pb((2ll*i)<<29);
l+=C;
for(;;l+=C)
{
l=min(l,r+1),v.pb((l*2ll-1)<<29);
if(l-1>=r) break;
}
return collisions(v);
}
long long qmi(long long a,long long b)
{
long long res=1;
fo(i,1,b) res*=a;
return res;
}
int hack()
{
long long B=sqrt(1e9);
long long l=1e9/6+1,r=5e8;
while(l<r)
{
long long mid=l+r>>1;
if(get2(l,mid)) r=mid;
else l=mid+1;
}
l=l*2-1;
l<<=29;
vector<long long> v;
long long now=l;
for(long long i=2;i*i<=l;i++) if(l%i==0)
{
long long ori=l;
long long cnt=0;
while(l%i==0) l/=i,cnt++;
long long ll=0,rr=cnt;
while(ll<rr)
{
long long mid=ll+rr+1>>1;
if(collisions({1,now/qmi(i,mid)+1})) ll=mid;
else rr=mid-1;
}
now/=qmi(i,ll);
// cout<<now<<endl;
}
if(l>1)
{
if(collisions({1,now/l+1})) now/=l;
}
return now;
}
次数:88186。
相关推荐
评论
共 21 条评论,欢迎与作者交流。
正在加载评论...