[EGOI 2023] Guessing Game / 猜猜看
[EGOI 2023] Guessing Game / 猜猜看
下文称安娜为 A,贝蒂尔为 B,埃玛为 E。
\(K = N - 1\)
考虑 A 在第 \(i\) 次访问的房子中填入 \(i\),则 E 必然填入一个重复数字,找出并询问即可。
\(K = \dfrac{N}{2}\)
将所有房子分成左右两部分,并对每部分分别进行标号,进行分讨:
-
若在一个部分中出现两个相同的数,则询问这两个数
-
若两个部分的中数都两两不同,则询问 \(50000\) 出现的位置
这样做的正确性是显然的,且这种方法容易借鉴,下面再举一个例子。
\(K = \dfrac{N}{10} + 8\)
将所有房子分成 \(10\) 块,按照上面的方法做。
然而此时会出现多个极大值,无法进行询问区分。
考虑将每个块的最后一个值从 \(10000\) 开始标号,只会标到 \(10008\),此时进行分讨:
-
若在一个部分中出现两个相同的数,则询问这两个数
-
若每块的最大值中出现两个相同的数,则询问这两个数
由此我们可以推出,将房子分成 \(B\) 块,则 \(K = O(B + \dfrac{N}{B})\)。
进行平衡,可以得到 \(K = O(\sqrt{N})\) 的做法。
\(K = O(\log N)\)
考虑刚才我们分的是单层块,现在我们改为分多层块。
容易想到一种多层分块的数据结构,即线段树,考虑在线段树上进行操作。
上文中每块的最后一个元素具有特殊性,我们类比的找到 \(i\) 作为最后一个元素的最长线段。
假设该线段在线段树上的深度为 \(x\),则将 \(A_i\) 设为 \(x\),下面是例子:

考虑若 B 得到的序列包含 \(1\),则其必然为 C 填入的数字。同理,找不到对应节点同上。
否则我们举一个例子:

红色线段的两个子节点均被指向,则这两者必有一个被 E 的房子指向。
如此操作,我们就得到了 \(K = O(\log N)\) 的做法。
此时我们并没有用到 \(1\),所以可以将所有数整体减 \(1\)。
\(K = 7\)
已经不是人类了。
在前 \(N - 32\) 个访问到的房子上写上 \(7\),接下来我们分情况讨论。
假设 E 没有写数字 \(7\),则需要在 \(32\) 个房子中找到 E 的房子,沿用上文方法可以解决。
假设 E 写了数字 \(7\),我们考虑通过那 \(32\) 个房子的总和推算出 E 的房子编号。
考虑传输这个总合 \(S\),我们有 \(32\) 个位置,想到传输 \(2^{16}\) 以内的二进制数。
我们求出 \(S \bmod 2^{16}\),显然我们容易从剩余的点中提取出 \(S\)。
具体提取方式,我们将每位传两次,取奇数位即可。
我们会得到两种可能的 \(S \bmod N\),依次询问对应的答案即可。
现在考虑如何将 \(S\) 映射到对应的值上,发现相邻两位必有 \(5\),将其变成 \(6\) 即可。
#include<iostream>
#include<cstdio>
using namespace std;
const int MOD=65536;
int P,N;
bool flag[100010];
int idt[100010],pos[40],id_pos[40],rev[40];
struct Node{
int l,r,depth,fa,remain,do_p;
}a[210];
void build(int id,int l,int r,int depth,int fa){
a[id].l=l;
a[id].r=r;
a[id].depth=depth;
a[id].fa=fa;
if(l==r){
id_pos[l]=id;
}
else{
int mid=(l+r)>>1;
build(id*2,l,mid,depth+1,id);
build(id*2+1,mid+1,r,depth+1,id);
}
}
void Subtask_1(){
cout<<7<<endl;
for(int i=1;i<=N-32;i++){
int id;
cin>>id;
cout<<7<<endl;
flag[id]=true;
}
int tot=0,S=0;
for(int i=0;i<N;i++){
if(!flag[i]){
tot++;
idt[i]=tot;
pos[tot]=i;
S+=i;
S%=MOD;
}
}
for(int i=1;i<=32;i++){
int pre=(i-1)/2;
if(S&(1<<pre)){
rev[i]=true;
}
else{
rev[i]=false;
}
}
build(1,1,32,0,0);
for(int i=1;i<=31;i++){
int id;
cin>>id;
int pos_now=idt[id];
int pre=id_pos[pos_now],num;
while(pre){
a[pre].remain++;
if(a[pre].remain==a[pre].r-a[pre].l+1){
num=a[pre].depth;
}
pre=a[pre].fa;
}
if(num==5){
if(rev[pos_now]){
num=6;
}
}
cout<<num<<endl;
}
}
int A[100010];
void Subtask_2(){
int cnt=0;
for(int i=0;i<N;i++){
cin>>A[i];
if(A[i]==7){
cnt++;
}
}
if(cnt==N-31){
int tot=0,sum=0,S=0,pre=0;
for(int i=0;i<N;i++){
if(A[i]!=7){
sum+=i;
sum%=MOD;
if(A[i]>=5){
if(A[i]==6){
S+=(1<<pre);
}
pre++;
}
}
}
int tmp=((S-sum)%MOD+MOD)%MOD;
int tmp2=tmp+MOD;
if(tmp2<N){
cout<<tmp<<" "<<tmp2<<endl;
}
else{
cout<<tmp<<" "<<tmp<<endl;
}
}
else{
int tot=0;
for(int i=0;i<N;i++){
if(A[i]==6){
A[i]=5;
}
if(A[i]!=7){
tot++;
idt[i]=tot;
pos[tot]=i;
}
}
build(1,1,32,0,0);
for(int i=0;i<N;i++){
if(A[i]!=7){
int pos_now=idt[i];
int pre=id_pos[pos_now];
while(pre){
if(a[pre].depth==A[i]){
a[pre].do_p=i+1;
break;
}
pre=a[pre].fa;
}
int u=a[pre].fa;
if(a[u*2].do_p && a[u*2+1].do_p){
cout<<a[u*2].do_p-1<<" "<<a[u*2+1].do_p-1<<endl;
return ;
}
}
}
}
}
int main(){
cin>>P>>N;
if(P==1){
Subtask_1();
}
else{
Subtask_2();
}
return 0;
}

浙公网安备 33010602011771号