专栏文章
题解:CF2156D Find the Last Number
CF2156D题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min8z8aq
- 此快照首次捕获于
- 2025/12/01 22:31 3 个月前
- 此快照最后确认于
- 2025/12/01 22:31 3 个月前
喜提 个 WA!
我们很显然有一个 次查询的做法,设 表示 的第 位的值,显然我们只需要求 的所有第 就能知道 的值,求出 第 位的方法很简单,根据简单原理得出:
然后仔细观察,发现可以用一种神奇的方法给等式右边同时减去那些 位有和 不相同的的数,你就会得到这个:
这里 指的是 都和 相同的数所构成的集合, 指的是 都和 相同的 的下标 所构成的集合。
然后你就会发现,这个东西的询问次数其实是:
虽然这玩意看似在 比较极限的时候会超出 一点点,但这只是很粗略的上界,实际明显低于这个。
代码:
CPP#include<bits/stdc++.h>
using namespace std;
const int N = 2e4+5;
const int mx = 2e4;
int a[N];
int b[N];
int bx[N];
int by[N];
int P[N];
int hou[N];
int dian[N];
int NP[N][2];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
for(int i = 1;i<=mx;++i)
{
int s = (i&1);
b[i] = b[i-1]+s;
bx[i] = bx[i-1];
by[i] = by[i-1];
if(s)
{
bx[i]+=((i&2)>0);
}
else
{
by[i]+=((i&2)>0);
}
}
int _;
cin >> _;
while(_--)
{
int n;
cin >> n;
int cnt = 0,numx = 0;
for(int i = 1;i<n;++i)
{
cout << "? " << i << " 1" << endl;
cin >> dian[i];
cnt+=dian[i];
}
int w = b[n]-cnt;
hou[0] = w;
for(int i = 1;i<n;++i)
{
if(dian[i] == w)
{
P[++numx] = i;
}
}
int ans = w;
int s = 31-__builtin_clz(n);
int num1 = 0,num2 = (w == 0?by[n]:bx[n]);
int nx = 0,ny = 0;
for(int i = 1;i<=numx;++i)
{
cout << "? " << P[i] << " 2" << endl;
int x;
cin >> x;
num1+=(x>0);
if(!x)
{
NP[++nx][0] = P[i];
}
else
{
NP[++ny][1] = P[i];
}
}
for(int i = 1;i<=s;++i)
{
ans+=(1<<i)*(num2-num1);
if(i == s)
{
continue;
}
w = num2-num1;
hou[i] = w;
for(int j = 1;j<=(w?ny:nx);++j)
{
P[j] = NP[j][w];
}
numx = (w?ny:nx);
nx = 0,ny = 0;
num1 = 0;
for(int j = 1;j<=numx;++j)
{
cout << "? " << P[j] << ' ' << (1<<i+1) << endl;
int x;
cin >> x;
num1+=(x>0);
if(!x)
{
NP[++nx][0] = P[j];
}
else
{
NP[++ny][1] = P[j];
}
}
num2 = 0;
for(int j = 1;j<=n;++j)
{
int dui = 1;
for(int k = 0;k<=i;++k)
{
int flag = ((j&(1<<k))>0);
if(flag!=hou[k])
{
dui = 0;
break;
}
}
if(dui)
{
num2+=((j&(1<<i+1))>0);
}
}
}
cout << "! " << ans << endl;
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...