服装(交互题)
内核
- 注意:需要
fflush(stdout)
或cout << endl
刷新输出缓冲区。 - 交互题的套路:分治、二分、增量法。
题意简述
给定序列长度 \(n \in [2,150]\),你可以在一次询问里得到某一些点的本质不同颜色个数,格式为 k a_1 a_2 a_3 ... a_k
。
让你确定序列每一个位置的颜色。询问次数要求 \(\le 3500\)。
分析
这里使用了增量法和倍增。考虑我们已经确定了前 \(i-1\) 个位置。(询问 \([l,r]\) 的结果记为 \(ask[l][r]\))
- 若 \(ask[1][i] = ask[1][i-1] + 1\),则说明前 \(i-1\) 个中没有与 \(a_i\) 相同颜色的,记下颜色为
++tot
。 - 否则我们要找到与 \(a_i\) 相同的前一个位置,即找到最左边的一个 \(res\) 使得 \(ask[res][i] = ask[res][i-1] + 1\) 那么说明结果就是 \(res-1\)。那么并查集维护即可。
考试的时候没有方向,在乱搞。这里区间之间的关系与颜色的密切相关使我们使用了增量法,大大简化问题。
CODE
#include<bits/stdc++.h>
using namespace std;
const int N = 155;
int n,col[N];
int ask[N][N];
int qry(int l,int r){
if(l == r)return 1;
if(ask[l][r])return ask[l][r];
printf("%d ",r-l+1);
for(int i = l;i<=r;++i)printf("%d ",i);
fflush(stdout);
int res;
scanf("%d",&res);
return ask[l][r] = res;
}
int tot,fa[N];
int pre[N];
int find(int x){ return fa[x] == x ? x : fa[x] = find(fa[x]); }
int main(){
scanf("%d",&n);
for(int i = 1;i<=n;++i)fa[i] = i;
col[1] = ++tot;
for(int i = 2;i<=n;++i){
if(qry(1,i) == qry(1,i-1) + 1){
col[i] = ++tot;
}else{
int l = 1, r = i-1, res = i-1;
while(l <= r){
int mid = (l + r) >> 1;
if(qry(mid,i) == qry(mid,i-1) + 1){
r = mid - 1;
res = mid-1;
}else l = mid+1;
}
fa[i] = find(res);
}
}
printf("0 ");
for(int i = 1;i<=n;++i)printf("%d ",col[find(i)]);
fflush(stdout);
return 0;
}