Codechef Guessing Game
猜 x ≤ 1 e 9 x \leq 1e9 x≤1e9,你可以问 y y y是 < x , > x <x , >x <x,>x还是 = x =x =x,但是他可以对于 < < <和 > > >撒谎(说成另一种),但是不能连续两次撒谎,询问次数 ≤ 120 \leq 120 ≤120。
连续询问两次,每次询问可以得到两个区间,一个区间为撒谎时的区间,另一个为不撒谎时的区间。
这两个撒谎时的区间的交区间就可以排除掉。
那么我们第一次询问问区间
n
2
\frac n2
2n的位置,第二次问第一次撒谎区间的中点,则我们可以一次排除掉
n
4
\frac n4
4n的区间,询问次数为
2
log
4
3
1
e
9
=
140
2\log_{\frac 43} 1e9 = 140
2log341e9=140,卡不进去。
发现每两次询问,第二次询问的撒谎区间如果包含了第一次选的点,那么会有
n
2
\frac n2
2n大小的仍然合法的区间在第二次的撒谎区间内,在这
n
2
\frac n2
2n的区间内选中点作为第三次询问,像之前一样做又可以排除掉
n
4
\frac n4
4n的区间。(其实第三次也可以继续再做下去这样就是官方正解了,但是我求第三次之后没有继续也能过。)
那么这样可以三次询问排除掉
n
2
\frac n2
2n的区间,相当于一次询问能排除掉
0.20628113542798557582164679862673
n
0.20628113542798557582164679862673n
0.20628113542798557582164679862673n的区间。
但是如果第二次询问的撒谎区间不包含了第一次选的点,则还是只能一次排除掉
0.25
n
0.25n
0.25n的区间,一次询问是能排除
0.13397459621556135323627682924706
n
0.13397459621556135323627682924706n
0.13397459621556135323627682924706n的区间。
平摊一下复杂度。
假设我们第二个询问的点定在
n
k
\frac nk
kn处,则第一种会剩下的是
(
n
4
+
n
k
)
1
3
(\frac n4 + \frac nk)^{\frac 13}
(4n+kn)31的,
第二种是
(
n
−
n
k
)
1
2
(n - \frac nk)^{\frac 12}
(n−kn)21的。
反正取
k
=
5
3
k = \frac 53
k=35是可以过的。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
set<pii>st;
int n;
int qry(int x){
printf("%d\n",x);
fflush(stdout);
static char s[3];
scanf("%s",s);
if(s[0] == 'E') exit(0);
if(s[0] == 'L') return 0;
return 1;
}
int find(int x){
for(pii u:st){
if(u.second - u.first + 1 >= x)
return u.first + x - 1;
x -= u.second - u.first + 1;
}
assert(0);
return -1;
}
set<pii>::iterator split(int l){
set<pii>::iterator it = st.upper_bound(mp(l,0x3f3f3f3f));
it--;
if((*it).first == l) return it;
int L = (*it).first , R = (*it).second;
st.erase(it);
st.insert(mp(L,l-1));
return st.insert(mp(l,R)).first;
}
void erase(int l,int r){
int t = r - l + 1;
l = find(l) , r = find(r);
set<pii>::iterator itr = split(r+1) , itl = split(l);
for(;itl != itr;) st.erase(itl++);
n -= t;
}
int main(){
scanf("%d",&n);
st.insert(mp(1,n));
for(;n >= 3;){
int m = (1+n) >> 1;
int a = find(m);
int ta = qry(a);
if(ta == 1){
int m1 = max(1,(1 + m-1) * 3 / 5);
int b = find(m1);
int tb = qry(b);
if(tb == 1){
erase(m,m);
erase(1,m1);
}
else{
int m2 = (m+1+n) >> 1;
int c = find(m2);
int tc = qry(c);
if(tc){
erase(m1,m2);
}
else{
erase(m2,n);
erase(m1,m);
}
}
}
else{
int m1 = min(n,m+1+(n-m)*2/5);
int b = find(m1);
int tb = qry(b);
if(tb == 0){
erase(m1,n);
erase(m,m);
}
else{
int m2 = (1+m-1) >> 1;
int c = find(m2);
int tc = qry(c);
if(tc == 0){
erase(m2,m1);
}
else{
erase(m,m1);
erase(1,m2);
}
}
}
}
rep(i,1,n) qry(find(i));
}
CF1354G Find a Gift
用随机化求出第一个是否是礼品。
如果是石头的话就可以直接倍增求答案了。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define maxn 1005
using namespace std;
int n,K;
int Ran(int a,int b){ return rand() % (b-a+1) + a; }
int ask(int a,int b,int c,int d){
printf("? %d %d\n",b-a+1,d-c+1);
for(int i=a;i<=b;i++) printf("%d%c",i," \n"[i==b]);
for(int i=c;i<=d;i++) printf("%d%c",i," \n"[i==d]);
fflush(stdout);
char s[100];
scanf("%s",s);
if(s[0] == 'F') return 1;
if(s[0] == 'S') return -1;
if(s[0] == 'E') return 0;
exit(1);
}
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&K);
bool fg = 0;
for(int i=1;i<=29;i++){
int t = Ran(2,n);
if(ask(1,1,t,t) < 0){
fg = 1;
break;
}
}
if(fg){
printf("! %d\n",1);
fflush(stdout);
continue;
}
for(int i=1,s=1;s<=n;i++,s<<=1){
if(s*2 > n || ask(1,s,s+1,s<<1) != 0){
int t = s >> 1;
for(i--;i>=1;i--){
if(s+t <= n && ask(1,t,s+1,s+t) == 0) s += t;
t >>= 1;
}
printf("! %d\n",s+1);
fflush(stdout);
break;
}
}
}
}
CodeChef Guess the Prime!
标算被吊着打
取
x
=
2
15
x = 2^{15}
x=215,
t
=
x
2
m
o
d
P
t = x^2 \bmod P
t=x2modP,
k
=
x
2
−
t
k = x^2 - t
k=x2−t
则若
t
=
0
t = 0
t=0 , 则
P
=
2
P = 2
P=2。
否则
P
P
P为奇数,可以把
k
k
k用一个奇数
r
r
r表示成
(
k
=
r
×
2
a
)
m
o
d
P
=
0
(k = r \times 2^a) \bmod P = 0
(k=r×2a)modP=0
也就是
r
m
o
d
P
=
0
r \bmod P = 0
rmodP=0
所以
r
+
1
2
m
o
d
P
=
1
2
\frac {r+1}2 \bmod P = \frac 12
2r+1modP=21,也就是我们找到了
m
o
d
P
\bmod P
modP意义下
2
2
2的逆元(注意这个逆元不唯一,所以无法直接推出
P
P
P)。
那么我们求出
y
=
x
2
m
o
d
P
y = x^2 \bmod P
y=x2modP,这个
y
y
y就是在
m
o
d
P
\bmod P
modP意义下唯一的
4
4
4的逆元。
对于
P
=
4
k
+
1
P = 4k+1
P=4k+1型质数,
y
=
3
P
+
1
4
y = \frac {3P+1}4
y=43P+1,
P
=
4
y
−
1
3
P = \frac {4y-1}3
P=34y−1
对于
P
=
4
k
+
3
P = 4k+3
P=4k+3型质数,
y
=
P
+
1
4
y = \frac {P+1}4
y=4P+1,
P
=
4
y
−
1
P = 4y-1
P=4y−1
这两个不能同时满足,讨论一下即可。
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int ask(int a){
printf("1 %d\n",a);
fflush(stdout);
int r;scanf("%d",&r);
return r;
}
void ans(int a){
printf("2 %d\n",a);
fflush(stdout);
char s[10];
scanf("%s",s);
}
int main(){
int T;
for(scanf("%d",&T);T--;){
LL x = ask(1<<15);
if(!x) ans(2);
else{
for(x=(1<<30)-x;!(x&1);x>>=1);
LL y = ask((x+1)/2);
if((4 * y - 1) % 3 == 0 && y != 1) ans((4 * y - 1) / 3);
else ans(4 * y - 1);
}
}
}
CF 1372F. Omkar and Modes
S
o
l
v
e
(
l
,
r
)
:
Solve(l,r):
Solve(l,r):
1.查询
[
l
,
r
]
[l,r]
[l,r]中的众数
x
x
x,出现次数
f
f
f,
2.找到
x
x
x在
[
l
,
r
]
[l,r]
[l,r]中的一个出现位置
p
p
p。
3.若
[
p
−
f
+
1
,
p
]
[p-f+1,p]
[p−f+1,p]的众数为
x
x
x,则我们可以得到连续的
x
x
x的左端点,进而得到右端点,否则
[
p
,
p
+
f
−
1
]
[p,p+f-1]
[p,p+f−1]的众数一定为
x
x
x,同理得到。
4.将
x
x
x出现的位置答案数组赋值为
x
x
x,设
x
x
x的区间为
[
q
l
,
q
r
]
[ql,qr]
[ql,qr],递归解决
S
o
l
v
e
(
l
,
q
l
−
1
)
,
S
o
l
v
e
(
q
r
+
1
,
r
)
Solve(l,ql-1),Solve(qr+1,r)
Solve(l,ql−1),Solve(qr+1,r)
上面这个过程中除开
2
2
2以外的操作合计次数是
3
k
3k
3k,意思是我们要用一次求出
x
x
x在
[
l
,
r
]
[l,r]
[l,r]中的一个出现位置。
首先如果
a
a
a处不是
x
x
x,那么如果我们增加
a
a
a继续判断,可以一次增加
f
f
f,因为
x
x
x出现一定是长度为
f
f
f的一段。
其次,因为在
[
l
,
r
]
[l,r]
[l,r]中
x
x
x的长度最长,也就是说我们增加
f
f
f之后的位置的值不会和之前查询到位置在之前的任意一个值相同。
用
m
a
p
map
map维护一下。
显然如果当前值已经出现过,我们直接从
m
a
p
map
map取,
否则我们
+
=
f
+=f
+=f后的值之前都不会出现过(这里就需要我们从已知的值中最大的比
x
x
x小的值的右边开始枚举才能保证),所以均摊下来是
k
k
k次。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;
int n,ans[maxn];
map<int,int>mAp;
pair<int,int>ask(int l,int r){
l = max(l , 1) , r = min(r , n);
r = max(r , l);
printf("? %d %d\n",l,r);
fflush(stdout);
pair<int,int>R;
scanf("%d%d",&R.first,&R.second);
return R;
}
void Solve(int l,int r){
if(l > r) return;
auto u = ask(l,r);
auto it = mAp.upper_bound(u.first);
int x;int f,b;
if(it == mAp.begin()) x = l;
else if((*--it).first == u.first){
int p = (*it).second;
auto v = ask(p-u.second+1,p);
if(v.first == u.first){
f = p - v.second + 1,
b = f + u.second - 1;
}
else{
v = ask(p,p+u.second-1);
b = p + v.second - 1,
f = b - u.second + 1;
}
rep(i,f,b) ans[i] = u.first;
Solve(l,f-1) , Solve(b+1,r);
return;
}
else x = max(l , (*it).second + u.second);
int p;
for(;;x += u.second){
auto t = ask(x,x);
mAp[t.first] = x;
if(t.first == u.first){
p = x;
break;
}
}
auto v = ask(p-u.second+1,p);
if(v.first == u.first){
f = p - v.second + 1,
b = f + u.second - 1;
}
else{
v = ask(p,p+u.second-1);
b = p + v.second - 1,
f = b - u.second + 1;
}
rep(i,f,b) ans[i] = u.first;
Solve(l,f-1) , Solve(b+1,r);
}
int main(){
scanf("%d",&n);
Solve(1,n);
printf("!");
rep(i,1,n) printf(" %d",ans[i]);
fflush(stdout);
}