题意:
交互题,给你大小为
n
n
n 但具体元素未知的数组
A
A
A ,对应的密码组
P
P
P 与 数组
A
A
A 的关系:
P
i
=
A
1
∣
A
2
∣
.
.
.
∣
A
i
−
1
∣
A
i
+
1
∣
.
.
.
∣
A
n
P_i=A_1|A_2|...|A_{i-1}|A_{i+1}|...|A_n
Pi=A1∣A2∣...∣Ai−1∣Ai+1∣...∣An
即
P
i
P_i
Pi 为数组
A
A
A 中除第
i
i
i 个元素外,其它所有元素的或;
现在你有至多
13
13
13 次询问,每次询问 格式:
?
m
x
1
x
2
.
.
.
x
m
?~m~x_1~x_2~...~x_m
? m x1 x2 ... xm
即询问 数组
A
A
A 中
A
x
1
∣
A
x
2
∣
.
.
.
∣
A
x
m
A_{x_1}|A_{x_2}|...|A_{x_m}
Ax1∣Ax2∣...∣Axm 的值;
最后需要求出密码组
P
P
P
分析:
不是二分,不是二分,不是二分;
考虑给从数组
A
A
A 的编号入手
- v a l [ i ] [ 0 ] val[i][0] val[i][0] 表示编号二进制形式下第 i i i 位为 0 0 0 的元素的或;
- v a l [ i ] [ 1 ] val[i][1] val[i][1] 表示编号二进制形式下第 i i i 位为 1 1 1 的元素的或;
(举个例子,
v
a
l
[
1
]
[
1
]
=
A
1
∣
A
3
∣
A
5
.
.
.
,
因
为
1
(
10
)
=
1
(
2
)
,
3
(
10
)
=
1
1
(
2
)
,
5
(
10
)
=
10
1
(
2
)
val[1][1]=A_1|A_3|A_5...,~因为 1_{(10)}=1_{(2)},3_{(10)}=11_{(2)},5_{(10)}=101_{(2)}
val[1][1]=A1∣A3∣A5..., 因为1(10)=1(2),3(10)=11(2),5(10)=101(2))
所有的编号二进制都是唯一的,所以对应的密码组只要取所有位的反位的元素或就可以了,例如
5
(
10
)
=
10
1
(
2
)
5_{(10)}=101_{(2)}
5(10)=101(2) ,那么
P
5
=
v
a
l
[
1
]
[
0
]
∣
v
a
l
[
2
]
[
1
]
∣
v
a
l
[
3
]
[
0
]
;
P_5=val[1][0]~|~val[2][1]~|~val[3][0];
P5=val[1][0] ∣ val[2][1] ∣ val[3][0];
预处理
v
a
l
val
val 数组,需要的询问是
2
∗
l
o
g
n
2*logn
2∗logn 次,明显不行,这里借用的是编号的思想,题目的限制询问次数是
13
13
13 ,
n
n
n 的规模上限是
1000
1000
1000 ,所以我们从
13
13
13个二进制位中选择
6
6
6 个
1
1
1 位来给数组编号,那么
C
13
6
>
1000
C_{13}^6>1000
C136>1000,所以必然可以给每个元素分到唯一的编号,那么就只需要进行刚好
13
13
13 次询问 (一个二进制位一次) ,然后和上面一样取反位;
代码:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define sd second
#define P pair<int,int>
#define ll long long
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define frep(i,a,b) for(int i=a;i>=b;i--)
const int N = 1000+10;
ll query(vector<int>G){
if(G.empty())return 0;
cout<<"? "<<G.size();
for(auto v:G)cout<<" "<<v;
cout<<endl;
fflush(stdout);
ll res; cin>>res;
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
vector<int>ask[13];
int n,m=13,cnt=0,num[N]; cin>>n;
rep(i,1,(1<<m)-1){
if(__builtin_popcount(i)!=6)continue;
num[++cnt]=i;
rep(j,0,m-1)if(((i>>j)&1)==0)ask[j].pb(cnt); //取反位询问
if(cnt==n)break;
}
ll w[13],ans[N]={0};
rep(i,0,m-1)w[i]=query(ask[i]);
rep(i,1,n)rep(j,0,m-1)if((num[i]>>j)&1)ans[i]|=w[j];
cout<<"!";
rep(i,1,n)cout<<" "<<ans[i];
}