Codeforces Round #648 (Div. 2) G. Secure Password

题意:

交互题,给你大小为 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=A1A2...Ai1Ai+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} Ax1Ax2...Axm 的值;
最后需要求出密码组 P P P

分析:

不是二分,不是二分,不是二分;
考虑给从数组 A A A 的编号入手

  1. v a l [ i ] [ 0 ] val[i][0] val[i][0] 表示编号二进制形式下第 i i i 位为 0 0 0 的元素的或;
  2. 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]=A1A3A5..., 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 2logn 次,明显不行,这里借用的是编号的思想,题目的限制询问次数是 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];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值