Codeforces Round #648 (Div. 2) ABCDEFG

Codeforces Round #648 (Div. 2)

Problem A

题意:

n*m的方格,0表示未填过,1表示填过,初始状态下可能有已经填过的格子。两人轮流填(Ashish 先手),且只能填 未填过 并且 对应行列都未出现填过 的格子,到谁填不了那个人就输了。


思路:

每填下一个1,就有清白的一行和清白的一列双双惨遭毒手。所以统计一下有几行干净的几列干净的,取两者较小的就是最多能凑的对数。


总结:

可能容易把能填的格子的要求看错吧,刚开始脑中自动脑补成周围一圈不能有填过的,然后看成上下左右不能有填过的。

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define fi first
#define se second
#define pb push_back
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _r(const int&x){scanf("%d",&x);}
void _r(const ll&x){scanf("%I64d",&x);}
void _r(const char*x){scanf("%s",x);}
void R(){}
template<class T,class... U>void R(const T&head,const U&...tail){_r(head);R(tail...);}
void _w(const char x){putchar(x);}
void _w(const char*x){printf("%s",x);}
void _w(const int x){printf("%d",x);}
void _w(const ll x){printf("%I64d",x);}
void _w(const double x){printf("%.6f",x);}
void W(){}
template<class T,class...U>void W(const T&head,const U&...tail){_w(head);putchar(sizeof...(tail)?' ':'\n');W(tail...);}
const ll mod = 1e9+7;
ll fp1(ll a,ll b){ll ans=1;while (b){if(b&1)ans*=a;b>>=1;a*=a;}return ans;}
ll fp2(ll a,ll b){ll ans=1;while (b){if(b&1)ans=ans*a%mod;b>>=1;a=a*a%mod;}return ans;}
ll Read(){
    ll res=0,flag=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-')flag = -1;ch = getchar();}
    while (ch>='0' && ch<='9'){res = res*10+ch-'0';ch = getchar();}
    return res*flag;
}
#define CL(x) memset(x,0,sizeof x)
    
const int N = 2e5+5;
    
int mp[55][55];
bool col[55],row[55];
    
int main()
{
    duozu{
        int n,m;
        R(n,m);
        for1(i,1,n)for1(j,1,m) R(mp[i][j]);
    
        for1(i,1,max(n,m)) col[i] = row[i] = true;
    
        for1(i,1,n)for1(j,1,m){
            if (mp[i][j]==1) row[i] = col[j] = false; 
        }
    
        int cntcol = 0,cntrow = 0;
    
        for1(i,1,n) if (row[i]==true) cntrow++;
        for1(i,1,m) if (col[i]==true) cntcol++;
        int ans = min(cntcol,cntrow);
    
        if (ans&1) W("Ashish");
        else W("Vivek");
    
    }
    return 0;
}

Problem B

题意:

给出一个数列,并给数列中每个数一个性别,性别不同的两个数可以交换位置,最后询问是否能通过交换位置将数列从小到大排列


思路:

只要男女都有,一定可以排序好。

只需证明:

1.只要拥有一个女的,任意两个男的一定可以通过女的交换位置

2.同时,这个女的可以排到任意两个男的之间



假设XY是男的,Z是女的

初始XZY <=> ZXY <=> YXZ <=> YZX(<=>表示可以互相转换) ,1成立

ZX XZ边界情况显然,ZXY<=>XZY<=>XYZ,2成立



因此男女共存必定可以排序,只有一个性别除非刚开始就排好否则GG


总结:

记记结论之类的

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define fi first
#define se second
#define pb push_back
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _r(const int&x){scanf("%d",&x);}
void _r(const ll&x){scanf("%I64d",&x);}
void _r(const char*x){scanf("%s",x);}
void R(){}
template<class T,class... U>void R(const T&head,const U&...tail){_r(head);R(tail...);}
void _w(const char x){putchar(x);}
void _w(const char*x){printf("%s",x);}
void _w(const int x){printf("%d",x);}
void _w(const ll x){printf("%I64d",x);}
void _w(const double x){printf("%.6f",x);}
void W(){}
template<class T,class...U>void W(const T&head,const U&...tail){_w(head);putchar(sizeof...(tail)?' ':'\n');W(tail...);}
const ll mod = 1e9+7;
ll fp1(ll a,ll b){ll ans=1;while (b){if(b&1)ans*=a;b>>=1;a*=a;}return ans;}
ll fp2(ll a,ll b){ll ans=1;while (b){if(b&1)ans=ans*a%mod;b>>=1;a=a*a%mod;}return ans;}
ll Read(){
    ll res=0,flag=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-')flag = -1;ch = getchar();}
    while (ch>='0' && ch<='9'){res = res*10+ch-'0';ch = getchar();}
    return res*flag;
}
#define CL(x) memset(x,0,sizeof x)
    
const int N = 2e5+5;
    
int a[N];
vector<int>v[2];
    
int main()
{
    duozu{
        int n;
        R(n);
    
        v[0].clear();
        v[1].clear();
    
        for1(i,1,n) R(a[i]);
        for1(i,1,n){
            int x;
            R(x);
            v[x].push_back(a[i]);
        }
    
        bool flag = true;
    
        if (v[0].size()==0){
            for0(i,1,v[1].size()) if (v[1][i]<v[1][i-1]) flag = false;
        }
        if (v[1].size()==0){
            for0(i,1,v[0].size()) if (v[0][i]<v[0][i-1]) flag = false;
        }
    
        W(flag?"YES":"NO");
    
    }
    return 0;
}

Problem C

题意:

AB两个数列,均包含1~n这n个数,都可以整体左右移动(比如右移1个单位就是下标i变成i-1的值,下标1变成n的值),询问转来转去可以得到的AB的最长相同子序列


思路:

n最大2e5,想想不可能是普通的求最长相同子序列的算法,那咋办呢。
结果没有注意到AB中的数都是1~n,那么意味着只能1对1匹配,那么只需要统计扭转不同次数能匹配的对数,取最多的即可。


总结:

信息用全,想想有些信息在此题中可以提供的信息

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define fi first
#define se second
#define pb push_back
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _r(const int&x){scanf("%d",&x);}
void _r(const ll&x){scanf("%I64d",&x);}
void _r(const char*x){scanf("%s",x);}
void R(){}
template<class T,class... U>void R(const T&head,const U&...tail){_r(head);R(tail...);}
void _w(const char x){putchar(x);}
void _w(const char*x){printf("%s",x);}
void _w(const int x){printf("%d",x);}
void _w(const ll x){printf("%I64d",x);}
void _w(const double x){printf("%.6f",x);}
void W(){}
template<class T,class...U>void W(const T&head,const U&...tail){_w(head);putchar(sizeof...(tail)?' ':'\n');W(tail...);}
const ll mod = 1e9+7;
ll fp1(ll a,ll b){ll ans=1;while (b){if(b&1)ans*=a;b>>=1;a*=a;}return ans;}
ll fp2(ll a,ll b){ll ans=1;while (b){if(b&1)ans=ans*a%mod;b>>=1;a=a*a%mod;}return ans;}
ll Read(){
    ll res=0,flag=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-')flag = -1;ch = getchar();}
    while (ch>='0' && ch<='9'){res = res*10+ch-'0';ch = getchar();}
    return res*flag;
}
#define CL(x) memset(x,0,sizeof x)
    
const int N = 2e5+5;
    
int a[N],b[N],mp[N],cnt[N];
    
int main()
{
    int n;
    R(n);
    for1(i,1,n) R(a[i]),mp[a[i]] = i;
    for1(i,1,n) R(b[i]),cnt[(mp[b[i]]+n-i)%n]++;
    
    int ans = 0;
    
    for1(i,0,n-1) ans = max(ans,cnt[i]);
    
    W(ans);
    
    return 0;
}

Problem D

题意:

n*m的迷宫,每一格有以下一种:好人 坏人 墙 空地。空地可以任你造墙,除了墙其他地方随意走。询问是否可以让所有好人走到(n,m)所有坏人走不到(n,m)。


思路:

坏人周围四格出现好人那么好人能走出去他也能走出去,此时直接NO。

坏人四周如果有空地,就变成墙。然后对于处理过后的地图BFS判断是否每一个好人都能走到即可。


总结:



#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define fi first
#define se second
#define pb push_back
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _r(const int&x){scanf("%d",&x);}
void _r(const ll&x){scanf("%I64d",&x);}
void _r(const char*x){scanf("%s",x);}
void R(){}
template<class T,class... U>void R(const T&head,const U&...tail){_r(head);R(tail...);}
void _w(const char x){putchar(x);}
void _w(const char*x){printf("%s",x);}
void _w(const int x){printf("%d",x);}
void _w(const ll x){printf("%I64d",x);}
void _w(const double x){printf("%.6f",x);}
void W(){}
template<class T,class...U>void W(const T&head,const U&...tail){_w(head);putchar(sizeof...(tail)?' ':'\n');W(tail...);}
const ll mod = 1e9+7;
ll fp1(ll a,ll b){ll ans=1;while (b){if(b&1)ans*=a;b>>=1;a*=a;}return ans;}
ll fp2(ll a,ll b){ll ans=1;while (b){if(b&1)ans=ans*a%mod;b>>=1;a=a*a%mod;}return ans;}
ll Read(){
    ll res=0,flag=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-')flag = -1;ch = getchar();}
    while (ch>='0' && ch<='9'){res = res*10+ch-'0';ch = getchar();}
    return res*flag;
}
#define CL(x) memset(x,0,sizeof x)
    
const int N = 2e5+5;
    
char mp[55][55];
bool vis[55][55];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
int n,m;
    
bool BFS(int x,int y){
    queue<pair<int,int> >que;
    vis[x][y] = true;
    que.push({x,y});
    bool flag = false;
    while (!que.empty()){
        pair<int,int> now = que.front();que.pop();
        //W("nowx =",now.fi,"nowy =",now.se);
        for1(i,0,3){
            int xx = dx[i] + now.fi;
            int yy = dy[i] + now.se;
            //W("?1",xx,yy);
            if (xx<1||xx>n||yy<1||yy>m) continue;
            if (vis[xx][yy]==true || mp[xx][yy]=='B' || mp[xx][yy]=='#') continue;
            vis[xx][yy] = true;
            //W("?2",xx,yy);
            que.push({xx,yy});
            if (xx==n&&yy==m) flag = true;
        } 
    }
    //W(flag?"YYYES":"NOOOO");
    return flag;
}
    
int main()
{
    duozu{
    
        R(n,m);
    
        for1(i,1,n) R(mp[i]+1);
    
        bool flag = true;
    
        for1(i,1,n)for1(j,1,m){
            vis[i][j] = false;//init
            if (mp[i][j]=='B'){
                if (i-1>0){
                    if (mp[i-1][j]=='G'){flag = false;} 
                    if (mp[i-1][j]=='.') mp[i-1][j] = '#';
                }
                if (i+1<=n){
                    if(mp[i+1][j]=='G'){flag = false;}
                    if (mp[i+1][j]=='.') mp[i+1][j] = '#';
                }
                if (j-1>0){
                    if(mp[i][j-1]=='G'){flag = false;}
                    if (mp[i][j-1]=='.') mp[i][j-1] = '#';
                }
                if (j+1<=m){
                    if(mp[i][j+1]=='G'){flag = false;}
                    if (mp[i][j+1]=='.') mp[i][j+1] = '#';
                }
                //if (!flag) W("x =",i,"y =",j);
            }
        }
    
        //for1(i,1,n) W(mp[i]+1);
    
        if (!flag){
            W("No");
            continue;
        }
    
        for1(i,1,n){
            for1(j,1,m){
                if (vis[i][j]==false&&mp[i][j]=='G'){
                    flag = BFS(i,j);
                    //W("x =",i,"y =",j);
                    if (!flag) break;
                }
            }
            if (!flag) break;
        }
        W(flag?"Yes":"No");
    
    }
    return 0;
}

Problem E

题意:

给出一个n个数的数列。定义从中取k个数得到的价值 = ∑ 2 i \sum2^i 2i (i为 k个数中 至少有max(k-2,1)个数 二进制第i位 是1 的所有数)。你可以随便从n个数中想取几个就去几个,你需要求出最大的价值。


思路:

我们可以发现如果k<=3,直接数据全部或运算一下即可。

如果k>3,我们会发现 对于每一个被计入答案 的位,最多有两个数 这一位是0。这说明什么呢,我们从这k中任取三个,答案中的每一位至少有一个数有。这说明什么呢,我们只要从k个任取三个结果都会大于等于这k个的价值。因此三层for循环暴力选三个计算最大价值做此题即可。


总结:

这波啊,这波是鸽巢原理。

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define fi first
#define se second
#define pb push_back
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _r(const int&x){scanf("%d",&x);}
void _r(const ll&x){scanf("%I64d",&x);}
void _r(const char*x){scanf("%s",x);}
void R(){}
template<class T,class... U>void R(const T&head,const U&...tail){_r(head);R(tail...);}
void _w(const char x){putchar(x);}
void _w(const char*x){printf("%s",x);}
void _w(const int x){printf("%d",x);}
void _w(const ll x){printf("%I64d",x);}
void _w(const double x){printf("%.6f",x);}
void W(){}
template<class T,class...U>void W(const T&head,const U&...tail){_w(head);putchar(sizeof...(tail)?' ':'\n');W(tail...);}
const ll mod = 1e9+7;
ll fp1(ll a,ll b){ll ans=1;while (b){if(b&1)ans*=a;b>>=1;a*=a;}return ans;}
ll fp2(ll a,ll b){ll ans=1;while (b){if(b&1)ans=ans*a%mod;b>>=1;a=a*a%mod;}return ans;}
ll Read(){
    ll res=0,flag=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-')flag = -1;ch = getchar();}
    while (ch>='0' && ch<='9'){res = res*10+ch-'0';ch = getchar();}
    return res*flag;
}
#define CL(x) memset(x,0,sizeof x)
    
const int N = 2e5+5;
    
ll a[500+5];
    
int main()
{
    int n;
    R(n);
    for1(i,1,n) R(a[i]);
    
    ll ans = 0;
    if (n==1) ans = a[1];
    if (n==2) ans = a[1]|a[2];
    if (n>2){
        for1(i,1,n)
            for1(j,i+1,n)
                for1(k,j+1,n)
                    ans = max(ans,a[i]|a[j]|a[k]);
    }
    W(ans);
    return 0;
}

Problem F

题意:

把数列a变成另一个数列b,都是n个数字。操作是选择k(k<=n/2),交换长度为k的前缀与长度为k的后缀,可进行无限次。询问是否可行


思路:

不会真有人 没发现不论怎么交换,前缀和后缀 相同位置的数 下标的和 都是n+1吧?
(我的做法有点捞,其实可以map<pair<左,右>,int>)


总结:

分析分析一步操作的本质,变了什么不变什么的。看来这样交换可以让每对数出现在任意位置(除了中间),不会证记住算了。

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define fi first
#define se second
#define pb push_back
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _r(const int&x){scanf("%d",&x);}
void _r(const ll&x){scanf("%I64d",&x);}
void _r(const char*x){scanf("%s",x);}
void R(){}
template<class T,class... U>void R(const T&head,const U&...tail){_r(head);R(tail...);}
void _w(const char x){putchar(x);}
void _w(const char*x){printf("%s",x);}
void _w(const int x){printf("%d",x);}
void _w(const ll x){printf("%I64d",x);}
void _w(const double x){printf("%.6f",x);}
void W(){}
template<class T,class...U>void W(const T&head,const U&...tail){_w(head);putchar(sizeof...(tail)?' ':'\n');W(tail...);}
const ll mod = 1e9+7;
ll fp1(ll a,ll b){ll ans=1;while (b){if(b&1)ans*=a;b>>=1;a*=a;}return ans;}
ll fp2(ll a,ll b){ll ans=1;while (b){if(b&1)ans=ans*a%mod;b>>=1;a=a*a%mod;}return ans;}
ll Read(){
    ll res=0,flag=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-')flag = -1;ch = getchar();}
    while (ch>='0' && ch<='9'){res = res*10+ch-'0';ch = getchar();}
    return res*flag;
}
#define CL(x) memset(x,0,sizeof x)
    
const int N = 2e5+5;
    
unordered_map<int,int>mp;
int a[N],b[N];
vector<int>v[505];
bool vis[505];
    
int main()
{
    duozu{
        int n;
        R(n);
        mp.clear();
        for1(i,1,n) R(a[i]);
        for1(i,1,n) R(b[i]);
    
        int id = 1;
    
        for1(i,1,n){
            vis[i] = false;
            if ( !mp.count(a[i]) ){
                mp[a[i]] = id;
                v[id].clear();
                v[id].pb(i);
                id++;
            }
            else {
                v[mp[a[i]]].pb(i);
            }
        }
        bool flag = true;
        if (n&1 && a[n/2+1]!=b[n/2+1]){
            flag = false;
        }
        else {
            for1(i,1,n/2){
                // 操作b[i]和b[n-i+1] 对应的mp,找一对和等于n+1的
                flag = false;
                for (auto lid:v[mp[b[i]]]){
                    if (!vis[lid]){
                        for (auto rid:v[mp[b[n-i+1]]]){
                            if (!vis[rid] && lid+rid==n+1){
                                flag = true;
                                vis[lid] = vis[rid] = true;
                                break;
                            }
                        }
                    }
                    if (flag) break;
                }
                if (!flag) break;
            }
        }
        W(flag?"Yes":"No");
    }
    return 0;
}

Problem G

题意:

交互题:

你可以cout打出问题询问系统,cin读入系统提供的信息。最后cout打出答案,系统判定过了你就过了。相当于番剧《如果能写小说就好了》第一话07:33的那个玩意儿,系统是那个黄头发戴眼镜的,你扮演旁边一群问问题猜答案的。



有n个你不知道的数a1~an,你需要知道(a2|a3…|an),(a1|a3…|an),(a1|a2|a4…an)…,就是a1或运算到an去掉ai。你被允许最多询问13次,每次可以询问任意几个的或运算和。最多13次询问之后给出上面的所有答案。


思路:

首先不可能每次问一个,n最大是1000.

或运算在此题用到的一个性质是一个数不论被计算几次和一次的效果是一样的。 例如a|a|b|b|b = a|b

2logN的做法(虽然次数还是超,不过可以用于与OK的方法比较)

我们的做法比较粗糙的描述一下是这样:我们询问一些下标的OR和,诶嘿,巧了,因为提问的每堆OR的下标非常牛逼,导致我可以用其中几个OR和 利用上面那条性质 确保不包含ai的同时包含其他所有。



具体是这样我们挨个处理每个下标,转化为二进制,如果第i位是1,把这个id放到“所有第i位为1的询问中”,反之放到“所有第i位为0的询问中”。

然后我们神奇的发现,想求去掉ai的OR和,只需要找每个下标(除了i)和i不一样的一个二进制位,找到对应的询问,就能不把i算入而把那个下标算进去。

我们不禁会想,为什么不能只询问“所有第i位为1的询问”,还要处理“所有第i位为0的询问”,毕竟找到“i那位不为1那个下标那位为1”的询问不就好了吗?这样的话题就能过了?主要是还有这种情况:i=11101,那个下标 = 10001,此时只能使用0的询问。

总结:2logn = 20>13,失败



13次的方法:

思想同上一种做法一样,但是我们需要改变还要询问0的情况。于是聪明的出题人想到13次询问那么有13个二进制位。可以给每个数分配一个id,这个id二进制13位=6个1+7个0,每个数1的位置都不一样(废话不然怎么区别两个id),为什么一定要6+7呢,主要是因为这样产生的不同id最多(C(13,7)/C(13,6)方法数最多>1000,即C(n,n/2)方法数最多)。这样分配id的好处——终于不用担心出现其中某一个id的1另一个id中都有的情况了! 11101,10001这样的尴尬情况已经不会有了。



此时我们只处理出“所有第i位为1的询问”,然后求没有ai的OR和,只需要根据i分配的新id,把剩余的不包含当前id的7询问OR起来即可。(为什么剩余7个包含其他所有,因为要想不包含一个id,需要选取各不相同的7个询问的OR和,现在的7个询问只是不包含i的,类似鸽巢定理吧大概)


总结:

OR的这条性质好好用啊,位运算的题目我又见到新的题型了。

#include<bits/stdc++.h>

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define fi first
#define se second
#define pb push_back
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _r(const int&x){scanf("%d",&x);}
void _r(const ll&x){scanf("%I64d",&x);}
void _r(const char*x){scanf("%s",x);}
void R(){}
template<class T,class... U>void R(const T&head,const U&...tail){_r(head);R(tail...);}
void _w(const char x){putchar(x);}
void _w(const char*x){printf("%s",x);}
void _w(const int x){printf("%d",x);}
void _w(const ll x){printf("%I64d",x);}
void _w(const double x){printf("%.6f",x);}
void W(){}
template<class T,class...U>void W(const T&head,const U&...tail){_w(head);putchar(sizeof...(tail)?' ':'\n');W(tail...);}
const ll mod = 1e9+7;
ll fp1(ll a,ll b){ll ans=1;while (b){if(b&1)ans*=a;b>>=1;a*=a;}return ans;}
ll fp2(ll a,ll b){ll ans=1;while (b){if(b&1)ans=ans*a%mod;b>>=1;a=a*a%mod;}return ans;}
ll Read(){
    ll res=0,flag=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-')flag = -1;ch = getchar();}
    while (ch>='0' && ch<='9'){res = res*10+ch-'0';ch = getchar();}
    return res*flag;
}
#define CL(x) memset(x,0,sizeof x)

const int N = 2e5+5;

const int Q = 13;
#define vint vector<int>
#define vll vector<ll>

int main()
{
    int n;
    cin >> n;

    vector<vint>query(Q);
    vint mask(n);
    vll ans(n,0),qans(Q,0);

    int idx = 0;

    for (int i=1;i < (1<<Q);i++){//最多13位最大的数,是2^13 - 1
        if (__builtin_popcount(i)!=Q/2) continue;
        mask[idx] = i;
        for0(j,0,Q){
            if ((i>>j&1) == 1) query[j].pb(idx);//>> & ?
        }
        idx++;
        if (idx==n) break;
    }

    for0(i,0,Q){
        if (!query[i].empty()){
            cout << "? " << query[i].size();
            for (int it:query[i]) 
                cout << " " << it+1;
            cout << endl;

            fflush(stdout);

            cin >> qans[i];
            
        }
    }

    for0(i,0,n){
        for0(j,0,Q){
            if ((mask[i]>>j&1)==0) ans[i] |= qans[j];
        }
    }

    cout << "!";
    for0(i,0,n) cout << " " << ans[i];
    cout << endl;

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值