内核
- 注意:需要
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;
}