题目
传送门 to luogu
题目背景 今天的比赛又是小游戏
A
K
AK
AK 的万千比赛中平淡无奇的一场。
本来他听说最近一个叫
t
?
i
s
t
\sf t\cdots ist
t?ist 的白俄罗斯人挺嚣张的,就准备出一道题来震慑他。但是
D
D
(
X
Y
X
)
\sf DD(XYX)
DD(XYX) 还有校董会要开,所以他打消了这个念头,开始准备他的讲稿。
题目描述 校董会上,
D
D
(
X
Y
X
)
\sf DD(XYX)
DD(XYX) 要大家猜他每天都
A
K
AK
AK 了几场比赛。已知第
i
i
i 天是
a
i
a_i
ai? 场,一共
n
n
n 天,满足
1
?
a
i
?
n
1\leqslant a_i\leqslant n
1?ai??n 且任意两天的
A
K
AK
AK 场数不相同。
你只可以问他两种问题(为什么?因为校长就是这么规定的):
- 询问
a
x
?
m
o
d
?
a
y
a_x\bmod a_y
ax?moday? 的值。注意你必须满足
a
x
>
a
y
a_x>a_y
ax?>ay?,否则
a
x
a_x
ax? 就直接暴露了,不符合
D
D
(
X
Y
X
)
\sf DD(XYX)
DD(XYX) 装弱麻痹对手的原则;
- 询问他有多少天打了不小于
p
p
p 场比赛,来评估他对学校管理有多不在乎。也即
{
i
??
∣
??
a
i
?
p
,
??
i
∈
S
}
\{i\;|\;a_i\geqslant p,\;i\in S\}
{i∣ai??p,i∈S},其中
p
∈
[
1
,
n
]
p\in[1,n]
p∈[1,n] 为你给定的参数,
S
S
S 为你给定的集合。注意询问的是至少打
p
p
p 场比赛,而
a
i
a_i
ai? 是
A
K
AK
AK 场数。
现在,你可以问
m
1
m_1
m1? 次
a
x
?
m
o
d
?
a
y
a_x\bmod a_y
ax?moday? 的值,和
m
2
m_2
m2? 次集合内不小于
p
p
p 的数,但是要满足
∑
∣
S
∣
?
m
3
\sum|S|\leqslant m_3
∑∣S∣?m3? 。
请你猜出
D
D
(
X
Y
X
)
\sf DD(XYX)
DD(XYX) 每天的
A
K
AK
AK 场数吧!话说我为什么要猜这个?
数据范围与提示 对于一组数据,满足
n
=
m
1
=
m
3
=
4
n=m_1=m_3=4
n=m1?=m3?=4 而
m
2
=
1
m_2=1
m2?=1 。
对于其余数据,限制最强的满足
n
=
m
1
=
1
3
m
3
=
5
×
1
0
4
n=m_1=\frac{1}{3}m_3=5\times 10^4
n=m1?=31?m3?=5×104 而
m
2
=
15
m_2=15
m2?=15 。
思路
发现
m
2
<
log
?
2
n
m_2<\log_2 n
m2?<log2?n,考虑二分?找到了
n
2
\frac{n}{2}
2n?,那么所有
x
>
n
2
x>\frac{n}{2}
x>2n? 都去取模它便可知晓。
但是我们需要两次
m
2
m_2
m2? 询问,问
?
n
2
\geqslant\frac{n}{2}
?2n? 和
?
n
2
+
1
\geqslant\frac{n}{2}+1
?2n?+1 的,找到对称差。这样
m
2
=
2
log
?
2
n
m_2=2\log_2n
m2?=2log2?n,完全过不了。
优化方式我确实没想到。出发点是,
m
1
=
n
m_1=n
m1?=n 则基本上每次
m
1
m_1
m1? 询问都可以唯一确定一个数。
显然
x
?
m
o
d
?
n
2
??
(
n
?
x
>
n
2
)
x\bmod\frac{n}{2}\;(n\geqslant x>\frac{n}{2})
xmod2n?(n?x>2n?) 是可以的。而你有没有想过反过来,
n
?
m
o
d
?
x
??
(
n
2
?
x
<
n
)
n\bmod x\;(\frac{n}{2}\leqslant x<n)
nmodx(2n??x<n) 也是可以的?
只要想到这一点,我们可以最初
m
2
m_2
m2? 询问一次确定
n
n
n 的位置,然后
m
1
m_1
m1? 询问确定了
[
n
2
,
n
)
[\frac{n}{2},n)
[2n?,n) 的位置,然后以
n
2
\frac{n}{2}
2n? 作为新的最大值,递归。
这样一来
m
1
=
?
log
?
2
n
?
+
1
m_1=\lceil\log_2n\rceil+1
m1?=?log2?n?+1,还是稍微多一点,怎么办?
计算一下发现,经过
m
2
=
15
m_2=15
m2?=15 次询问,
n
n
n 恰好为
4
4
4 。现在我们只有
m
1
m_1
m1? 询问,可以做吗?
事实上完全可以。因为
4
?
m
o
d
?
x
??
(
1
?
x
?
3
)
4\bmod x\;(1\leqslant x\leqslant 3)
4modx(1?x?3) 可以找到
3
3
3(唯一余数为
1
1
1 的数字),接下来的一次询问可以确定
1
1
1 或者
2
2
2,那么最后剩一个空位直接填好。
最后计算一下
m
3
m_3
m3? 。第一次用
n
n
n 问出
n
n
n 的位置,接下来是
n
+
n
2
+
n
4
+
?
+
1
=
2
n
n+\frac{n}{2}+\frac{n}{4}+\cdots+1=2n
n+2n?+4n?+?+1=2n,恰好是
3
n
3n
3n 。
代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int x){
if(x > 9) writeint(x/10);
putchar((x-x/10*10)^48);
}
const int MaxN = 50005;
int n, m1, m2, m3;
int a[MaxN];
void solve(int m,int loca){
a[loca] = m;
if(m == 1) return ;
if(m == 2)
rep(i,1,n) if(!a[i]){
a[i] = 1; return ;
}
if(m == 4){
rep(i,1,n) if(!a[i]){
putchar('!'), putchar(' ');
writeint(loca), putchar(' ');
writeint(i), putchar('\n');
fflush(stdout);
if(readint() == 1){
a[loca = i] = 3; break;
}
}
int lst = 0;
rep(i,1,n) if(!a[i]){
if(lst){
a[i] = 3-lst; break;
}
putchar('!'), putchar(' ');
writeint(loca), putchar(' ');
writeint(i), putchar('\n');
fflush(stdout);
lst = a[i] = readint()+1;
}
return ;
}
int p = (m+1)>>1;
putchar('?'), putchar(' ');
writeint(m-1);
rep(i,1,n) if(!a[i])
putchar(' '), writeint(i);
putchar(' '), writeint(p);
putchar('\n'); fflush(stdout);
for(int k=readint(); k; --k)
a[readint()] = p;
int id = 0;
rep(i,1,n) if(a[i] == p){
putchar('!'), putchar(' ');
writeint(loca), putchar(' ');
writeint(i), putchar('\n');
fflush(stdout);
a[i] = m-readint();
if(a[i] == m) a[i] = p;
if(a[i] == p) id = i;
}
solve(p,id);
}
int main(){
n = readint(), m1 = readint();
m2 = readint(), m3 = readint();
printf("? %d ",n);
rep(i,1,n) writeint(i), putchar(' ');
writeint(n), putchar('\n');
fflush(stdout); readint();
solve(n,readint()), putchar('A');
rep(i,1,n) putchar(' '), writeint(a[i]);
putchar('\n'); fflush(stdout);
return 0;
}
|