交互题好玩!
看到各测试点限制各不相同,考虑数据点分治。
约定记号
- 。
形式化题意
你需要猜出评测机里一个 中的正整数 。为此你需要构造一个长度 ,值域 的正整数序列。评测机会把该序列排序并把需要猜的数插入到正确位置。
设排序后序列为 。接下来你最多可以询问 次。每次询问需要给出两个下标集合 ,满足 且 。评测机会返回 和 的大小关系。请猜出评测机中的数。
Subtask1
传 。暴力枚举到第一个满足 的位置,则必有 。
Subtask2
传序列 。插入 排序后,必然有 。我们只需比较 与 的大小。具体地:
- 若 :,
- 若 :,
- 若 :,
Subtask3
注意到 ,考虑二进制拆分,一次询问确定一位。
我们传 。该序列满足 的性质。观潮到插入 之后,在 及其右侧该性质会被破坏。
那么我们从右往左找到最大的满足以上性质的 。那么 的第 位一定是 。然后依次尝试加上 即可确定剩余位。恰好询问 次。
Subtask4
如果直接套用 Subtask 3 的做法可以获得 分。由于 ,该做法没有前途。
注意到 ,而 。这强烈暗示我们采用三分做法,需要一次询问将搜索范围缩小至 。
发现 的限制非常宽松。构造序列 。注意到,该序列满足性质 。
维护 所在的下标区间 ,初始时 。
每次取区间三等分点 ,查询 。
- : 插在 段中。
- :原性质仍然成立,说明 插在 段中。
- : 插在 段中。
然后不断三分即可,注意边界和细节问题。
Code
#include "cake.h" #include #include #define rep(i,a,b) for(int i(a);i #define per(i,a,b) for(int i(a);i>b;--i) #define rept(i,a,b) for(int i(a);i<=b;++i) #define pert(i,a,b) for(int i(a);i>=b;--i) #define eb emplace_back using namespace std; vector<int> bake_cakes(int N,int W,int K){ if(K==1) return {1,2,3}; if(K==100){ vector<int> res(100); iota(res.begin(),res.end(),1); return res; } if(K==30){ vector<int> res(30); rep(i,0,30) res[i]=1< return res; } vector<int> res; rept(i,1,2187) res.eb(i); return res; } int find_tastiness(int m,int W,int K){ if(K==1){ int k=compare_tastiness({0,3},{1,2}); return k==-1?3:(k?1:2); } if(K==100){ rept(i,0,99){ if(!compare_tastiness({i},{i+1})) return i+1; } return 0; } if(K==30){ int ans=0,h=-1; pert(i,29,1){ vector<int> t(i); iota(t.begin(),t.end(),0); if(compare_tastiness(t,{i})==-1){ h=i; break; } } if(h==-1) return 1; ans|=1< pert(i,h-1,0){ vector<int> t; rep(i,0,30) if(ans>>i&1) t.eb(i); t.eb(i); int k=compare_tastiness(t,{h+1}); if(!k) return ans|1< if(k==-1) ans|=1< } return ans; } int l=0,r=2187,cur=729; while(l+1 int k=compare_tastiness({l+cur,r-cur},{l,r}); if(k==-1) r=l+cur; else if(!k) l+=cur,r-=cur; else l=r-cur; cur/=3; } return r; }