P9529 [JOISC 2022 Day4] 一流团子师傅 题解
1qaz234567pzy · · 题解
一道交互好题恶心题
感谢 Aaronwrq 大佬的帮助,题解有所借鉴 Jason0211 的题解。
思路
我们可以把每个串看成一个集合,所以总共有
一:每个集合内的每个团子的颜色互不相同。
二:同一种颜色的团子是按串串的编号从左到右放到每个串串中的。
所以现在问题变为了如何检验这个颜色的团子放在哪里。
而我们似乎只能用 Query(x[]) 函数来检验,但是我们总共只能调用 Query(x[]) 函数。而团子的总数最多有 Query(x[]) 函数的情况下来求出这个团子放在哪。
我们根据这些想到可以二分,因为根据性质二,这种颜色的团子一定是前面一段串串有,后面的串串全部都没有的,所以可以通过二分的方式来查找。而二分要调用大约 Query(x[]) 函数,而
我们先假设前几个串串上已经有了一些团子,就像这样:
那么对于下一个这种颜色的团子,如果我们放在这两个已经有这种颜色的串上,就像这样:
由于这种颜色的团子的个数等于总的串串数,所以如果我们放在这两个已经有这种颜色的串上的话,所有剩下的团子就没有办法组成剩下串串的个数个一流团子串。(建议自己在纸上画一下)欸,这不就可以用到 Query(x[]) 函数了吗!
我们设当前的团子放在第
我们就用这个进行二分,如果能做出至少
核心代码如下:
//注意是交互题
namespace {
int n,m;
vector<int> v[1000];
}
bool work(int h,int j)
{
vector<int> G;
for(int qqq=h+1;qqq<=n*m;qqq++)
{
G.push_back(qqq);
}
for(int qqq=j+1;qqq<=m;qqq++)
{
for(int qq=0;qq<v[qqq].size();qq++)
{
G.push_back(v[qqq][qq]);
}
}
return Query(G)>=m-j;
}
void Solve(int N, int M)
{
n=N;
m=M;
for(int qqq=1;qqq<=n*m;qqq++)
{
int ll=1,rr=m,mid,ans=-1;
while(ll<=rr)
{
mid=(ll+rr)>>1;
int www=work(qqq,mid);
if(www)
{
rr=mid-1;
ans=mid;//记录mid
}
else
{
ll=mid+1;
}
}
v[ans].push_back(qqq);
}
for(int qqq=1;qqq<=m;qqq++)
{
Answer(v[qqq]);
}
}
AC记录