[交互] [ad-hoc] P12541 [APIO2025] Hack!
posted on 2025-05-22 10:49:58 | under | source
介绍一下官解做法。
题意:有 \(n\) 个桶,你想知道 \(n\) 是多少。为此你可以进行若干次询问,每次你给出 \(m\) 个正整数,交互库依次将 \(x_i\) 插入第 \(x_i\bmod n\) 个桶中,每次插入产生原有元素个数的贡献,最后返回总贡献。询问互相独立。\(n\le 10^9\),要求 \(\sum m\le 110000\)。
首先观察到若贡献为正,说明必然存在两个元素满足它们的差为 \(n\) 的倍数。那么不难对问题拆解为:先得到询问 \(\{1,x+1\}\) 贡献为正,则 \(n\mid x\);然后依次确定 \(n\) 的每个质因子次幂。
第二部分是容易的,每次去掉一个质因子查询是否仍为倍数即可。步数是 \(2\log n\) 几乎忽略不计。着重考虑第一部分。
我们尝试集合分治,每次划分出一个存在冲突的小集合。
开始前,如何构造覆盖所有元素的集合呢?类似 BSGS,记 \(B\) 为块长,记 \(S_1=\{1\dots B\},S_2=\{B,2B,3B\dots \}\),\(S_1\cup S_2\) 显然覆盖所有数。不过只需考虑 \((5\times 10^8,10^9]\) 范围内的倍数(不在范围就不断翻倍),所以可令 \(S_2=\{5\times 10^8+1+B,5\times 10^8+1+2B\dots \}\)。
注意 \(S_1,S_2\) 之间一定有冲突,也就是两端分别在 \(S_1,S_2\)。
现在做划分,将 \(S_1\) 分为两部分 \(L,R\)。判定 \(L,S_2\) 之间是否有冲突,有则 \(S_1\gets L\);反之 \(R\) 与 \(S_2\) 之间必然存在冲突,令 \(S_1\gets R\)。
考虑判定。首先对 \(L\cup S_2\) 查询,若为零不可能存在冲突。冲突还可能产生在 \(L,S_2\) 内部,不过 \(L\) 内部产生冲突意味着所有桶被 \(L\) 元素塞满,那么肯定存在 \(L,S_2\) 之间的冲突,所以不用管。
不妨假设冲突都在 \(S_2\) 内部,注意到 \(S_2\) 是一个公差为 \(B\) 的等差数列,那么容易列出其内部的冲突总数的式子为 \(G(\frac {lcm(n,B)}{B},|S_2|)\)。其中 \(G(x,y)\) 指依次插入 \(1\dots y\) 到 \(x\) 个桶的总贡献,是可以 \(O(1)\) 计算的。同时 \(G\) 固定 \(y\) 具有单调性,那么二分 \(\frac {lcm(n,B)}B=k\) 即可。得到 \(lcm(n,B)=kB\),查询 \(\{1,kB+1\}\) 是否真的是 \(n\) 的倍数,是那么我们的任务已经完成 \(kB\) 就是所求,否则产生矛盾说明 \(L,S_2\) 之间必然有冲突。
同理可对 \(S_2\) 进行划分。
减少步数肯定 \(S_1\) 砍一刀、\(S_2\) 砍一刀,做一轮就是 \(\frac {|S_1|}2+|S_2|+\frac {|S_1|}{2}+\frac {|S_2|}2=|S_1|+\frac 32 |S_2|\)。我们取 \(B=27000\) 上面的式子比较平衡,分治的总操作数大概是 \(109554\)。可以接受。
后记:事实上直接二分答案然后大步小步就是对的,每次只检查一个区间代价是根号区间大小,每次区间大小还会折半,总次数大概 \(108000\),从任何方面完虐官解。
代码
#include "hack.h"
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int B = 27000;
int n;
long long collisions(std::vector<long long> x);
LL G(int x, int y){
int h = y / x;
return 1ll * (h + 1) * h / 2 * (y % x) + 1ll * h * (h - 1) / 2 * (x - y % x);
}
int getF(int S2, LL cnt){
int L = 1, R = 1e9 + 5, mid;
while(L + 1 < R){
mid = L + R >> 1;
if(G(mid, S2) >= cnt) L = mid;
else R = mid;
}
if(G(L, S2) == cnt) return 1ll * L * B;
return -1;
}
int hack(){
vector<LL> S1, S2;
for(int i = 1; i <= B; ++i) S1.push_back(i);
int x = 5e8 + 1 + B;
while(x <= 1e9) {S2.push_back(x); x += B;}
S2.push_back(x);
int len = -1;
vector<LL> SS1, SS2;
while(S1.size() > 1 || S2.size() > 1){
SS1 = S1, SS2 = S2;
if(S1.size() > 1){
vector<LL> Lc, L, R;
for(int i = 0; i < S1.size() / 2; ++i) L.push_back(S1[i]);
for(int i = S1.size() / 2; i < S1.size(); ++i) R.push_back(S1[i]);
Lc = L;
for(auto i : S2) Lc.push_back(i);
LL Lcnt = collisions(Lc);
if(Lcnt == 0) S1 = R;
else{
int res = getF(S2.size(), Lcnt);
if(res != -1)
if(collisions({1, res + 1}) > 0) {len = res; break;}
S1 = L;
}
}
if(S2.size() > 1){
vector<LL> Lc2, L2, R2;
for(int i = 0; i < S2.size() / 2; ++i) L2.push_back(S2[i]);
for(int i = S2.size() / 2; i < S2.size(); ++i) R2.push_back(S2[i]);
Lc2 = L2;
for(auto i : S1) Lc2.push_back(i);
LL Lcnt2 = collisions(Lc2);
if(Lcnt2 == 0) S2 = R2;
else{
int res2 = getF(L2.size(), Lcnt2);
if(res2 != -1)
if(collisions({1, res2 + 1}) > 0) {len = res2; break;}
S2 = L2;
}
}
}
if(len == -1) len = S2[0] - S1[0];
if(collisions({1, 1 + len}) == 0){
exit(0);
}
vector<int> pc;
int ljs = len;
for(int i = 2; i * i <= ljs; ++i)
if(ljs % i == 0){
while(ljs % i == 0) ljs /= i;
pc.push_back(i);
}
if(ljs > 1) pc.push_back(ljs);
for(auto i : pc){
while(len % i == 0){
if(collisions({1, len / i + 1}) > 0) len /= i;
else break;
}
}
return len;
}

浙公网安备 33010602011771号