正题
这题还挺好玩的,花了6h左右吧。ztO emofunc Orz
只有第三个subtask比其他人的更严格,其他的大致相同,最后一个subtask可以做到
O
(
n
2
l
o
g
2
w
)
O(n^2log_2 w)
O(n2log2w)。
Subtask1
十分的简单,只需要随便给一个点放一个就行,不选的那个就是最小的。
Subtask2
首先考虑第一次给每一个点都放一个,这样可以选出来
[
51
,
100
]
[51,100]
[51,100]。
然后再给这50个放两个,考虑什么样的点会被选到,假设先选了
[
1
,
50
]
[1,50]
[1,50],剩下50个球选
[
85
,
100
]
[85,100]
[85,100],然后开始调整决策,比较
1
1
1和
84
84
84(还剩下两个球,只需要多增添一个),然后比较
2
+
3
+
4
2+3+4
2+3+4和
83
83
83,…,可以写一个程序帮助自己实现。就会发现第二次会选出来
[
76
,
100
]
[76,100]
[76,100],第三次给这
25
25
25个球放
4
4
4个,选出来
[
92
,
100
]
[92,100]
[92,100],第四次给这
9
9
9个球放
11
11
11个,选出来
[
100
,
100
]
[100,100]
[100,100]
Subtask3
我认为的重头戏来了。
这个
S
u
b
t
a
s
k
Subtask
Subtask因为不严谨所以被好多人说错了二分界限,虽然可以用正确的次数得到正确的答案,但是没有深究其中的道理。
可以想到,如果要比较两个位置的值,就分别给这两个位置都放上
w
w
w个球,当有一方选而另一方不选的时候,答案就出来了。
我们先来确定当有
w
w
w个球时,两个位置都选的话,值满足什么样的条件,设两个值分别为
a
,
b
,
a
<
b
a,b,a<b
a,b,a<b。
当
w
=
1
w=1
w=1时,由于当前手中还有两个球,可以先选一个
b
b
b,
a
a
a至少要满足
>
1
+
2
=
3
\gt 1+2=3
>1+2=3时,
a
a
a才会被选。(此处虽然说的不严谨,仅仅提供了一种方式去猜
a
a
a的下界,但可以发现,实际上只用分两种情况来讨论,即
a
<
=
3
a<=3
a<=3或者
a
>
3
a>3
a>3,可以发现,在
a
<
=
3
a<=3
a<=3时,无论怎么取,取到的两个球都
≥
a
\geq a
≥a,在
a
>
3
a>3
a>3时,换掉的最小的两个球恰好是
1
+
2
=
3
1+2=3
1+2=3,所以
a
>
3
a>3
a>3这个条件是充分必要的,下面同理。)
当
w
=
2
w=2
w=2时,由于当前手中还有两个球,可以先用
1
1
1选一个
b
b
b,
a
a
a至少要满足
>
2
+
3
+
4
=
9
\gt 2+3+4=9
>2+3+4=9时,
a
a
a才会被选。
当
w
=
3
w=3
w=3时,由于当前手中还有两个球,可以先用
1
,
2
1,2
1,2选一个
b
b
b,
a
a
a至少要满足
>
3
+
4
+
5
+
6
=
18
\gt 3+4+5+6=18
>3+4+5+6=18时,
a
a
a才会被选。
以此类推,我们可以很轻易算出如果两个球都取的话满足的下界条件。
球数 | 下界 x x x(即 a , b > x a,b>x a,b>x) |
---|---|
1 | 3 |
2 | 9 |
3 | 18 |
4 | 30 |
5 | 45 |
6 | 63 |
7 | 84 |
8 | 108 |
我们再来讨论一下如果两者都不选的情况下,
a
,
b
a,b
a,b满足的上界大小。
这里提供一种计算上界的方法,即只用考虑
a
=
1
,
b
=
x
a=1,b=x
a=1,b=x的情况,若
a
≠
1
,
b
=
x
a\neq 1,b=x
a=1,b=x,这样会使得替换数的和变小,更可能替换
b
b
b,而我们需要找到的是一个保证的上界,所以我们只用考虑上界最松的限制,即当
a
=
1
,
b
=
x
a=1,b=x
a=1,b=x时。
当
w
=
1
w=1
w=1时,因为手中有两个球,所以必选一个,即
b
<
=
0
b<=0
b<=0。
当
w
=
2
w=2
w=2时,因为手中有两个球,只需要另外替换出一个就可以选,即
b
<
=
2
b<=2
b<=2
当
w
=
3
w=3
w=3时,因为手中有两个球,另外替换出两个就可以选,即
b
<
=
2
+
3
=
5
b<=2+3=5
b<=2+3=5
以此类推,我们可以很轻易算出如果两个球都取的话满足的上界条件。
球数 | 上界 x 1 x_1 x1(即 a , b < = x 1 a,b<=x_1 a,b<=x1) | 下界x_2(即 a , b > x 2 a,b>x_2 a,b>x2) |
---|---|---|
1 | 0 | 3 |
2 | 2 | 9 |
3 | 5 | 18 |
4 | 9 | 30 |
5 | 14 | 45 |
6 | 20 | 63 |
7 | 27 | 84 |
8 | 35 | 108 |
对于这个表格我们很轻易就可以看出,在
w
=
i
w=i
w=i时两个都选,那么在
w
=
i
+
1
w=i+1
w=i+1时就不可能两个都不选,而当
w
=
8
w=8
w=8时只可能不选或者一个选一个不选,在
w
=
1
w=1
w=1时,只可能都选或者一个选一个不选,所以就一定可以在
w
=
[
1
,
8
]
w=[1,8]
w=[1,8]中二分出一个答案。这样需要二分
4
4
4次,并不能满足条件。
一个简单的缩减二分范围的方法就是直接把
7
7
7去掉,因为当
w
=
6
w=6
w=6时两个都选的话,在
w
=
8
w=8
w=8时就不可能两个都不选,因为
63
>
35
63>35
63>35,那么就做完了。
实际上,我们可以将
w
=
x
w=x
w=x可以区分的答案写成
x
1
<
(
a
o
r
b
)
<
=
x
2
x_1<(a\ or\ b)<=x_2
x1<(a or b)<=x2
所以我们只需要找出若干个球使他们的并集包括
[
1
,
100
]
[1,100]
[1,100]就可以了。
但这还没完,因为可以通过尝试发现
w
∈
[
1
,
3
,
5
,
8
]
w\in [1,3,5,8]
w∈[1,3,5,8]时,会通过此题,也许你会问到
a
=
4
,
b
=
5
a=4,b=5
a=4,b=5的情况怎么办?,那是因为下界在
a
≠
1
,
b
=
x
a\neq 1,b=x
a=1,b=x时,是会变窄的,一个球数管理的答案也就变多了,想要找出所有的方案吗?自行提交
2
8
2^8
28次就可以找出来了,也许下一个最优解就是你。
Subtask4
十分简单,考虑怎么用 1 1 1次比较两个位置,在每一个位置上放上 100 100 100个球就可以了,这样他一定会选一个。
Subtask5
由于只给了
100
100
100次,考虑分治,每次想办法将一个值域区间用
1
1
1次的代价
[
l
,
r
]
[l,r]
[l,r]分成两部分。
我们考虑在值为
[
l
,
r
]
[l,r]
[l,r]的位置上放上
k
k
k个球,看一下是否选一部分不选一部分即可。
怎么计算
k
k
k?
显然满足二分性质,当
k
k
k越小的时候,越有可能取,
k
k
k越大的时候,越不可能取。
当
k
k
k可以使得区间全部被选时,增加答案,全部不被选时减小答案,否则找到合法的
k
k
k。
这样我们就可以用
n
log
2
w
n \log_2 w
nlog2w次找出答案,还是不行。
发现区间
[
l
,
r
]
[l,r]
[l,r]内放
k
k
k个球,其他不放,取最大代价这种事情可以自己干,只需要枚举区间内选几个,其他的贪心选就可以了,时间复杂度
O
(
n
)
O(n)
O(n)
所以总的尝试次数就变成
n
−
1
n-1
n−1了,时间复杂度就变成
O
(
n
2
log
2
w
)
O(n^2\log_2 w)
O(n2log2w)了。
由于
n
n
n才
100
100
100,所以博主直接写了暴力枚举。
#include<bits/stdc++.h>
using namespace std;
void playRound(int*,int*);
const int N=110;
int a[N],b[N],n,w;
int minValue(int n,int w){
a[0]=1;
playRound(a,b);
for(int i=0;i<n;i++) if(!b[i]) return i;
}
int maxValue(int n,int w){
for(int i=0;i<n;i++) a[i]=1;
for(int t=1;t<=4;t++){
playRound(a,b);
int tot=0;
for(int i=0;i<n;i++) if(b[i]>a[i] && a[i])
tot++;
for(int i=0;i<n;i++)
if(b[i]>a[i] && a[i]) a[i]=w/tot;
else a[i]=0;
if(tot==1) break;
}
for(int i=0;i<n;i++) if(a[i]) return i;
}
int greaterValue(int n,int w){
int l=1,r=4;
int op[10]={0,1,3,5,8};
while(l<=r){
int mid=(l+r)/2;
a[0]=a[1]=op[mid];
playRound(a,b);
if((b[0]>a[0]) ^ (b[1]>a[1])) return (b[0]>a[0])?0:1;
else if(b[0]>a[0]) l=mid+1;
else r=mid-1;
}
}
bool check(int x,int l,int r){
int mmax=l*(l-1)/2,pos=0,tot=w-(n-r)-x-1,ans=0;
for(int t=r;t>=l && tot>=0;t--,tot-=x+1){
ans+=t;
if(mmax<ans+(l-1+l-tot)*tot/2) mmax=ans+(l-1+l-tot)*tot/2,pos=t;
}
return pos!=0 && pos!=l;
}
int get_value(int l,int r){
for(int i=1;i<=w/(r-l+1);i++)
if(check(i,l,r)) return i;
}
void solve(int l,int r,vector<int>&op,int*num){
if(l==r){
num[op[0]]=l;
return ;
}
int k=get_value(l,r);
vector<int> L,R;
memset(a,0,sizeof(a));
for(int i=0;i<op.size();i++) a[op[i]]=k;
playRound(a,b);
for(int i=0;i<op.size();i++){
if(b[op[i]]>k) R.push_back(op[i]);
else L.push_back(op[i]);
}
solve(l,l+L.size()-1,L,num);
solve(l+L.size(),r,R,num);
}
void allValues(int N,int W,int*c){
vector<int> V;n=N;w=W;
for(int i=0;i<n;i++) V.push_back(i);
solve(1,n,V,c);
}