题意简述
有
n
n
n块蛋糕和三个小朋友Alice,Bob和Charlie,对于每个小朋友来说,每块蛋糕的价值是不同的。第
i
i
i块蛋糕对Alice,Bob,Charlie来说的价值分别是
a
i
a_i
ai,
b
i
b_i
bi,
c
i
c_i
ci(其中,
∑
i
=
1
n
a
i
=
∑
i
=
1
n
b
i
=
∑
i
=
1
n
c
i
=
t
o
t
)
\sum_{i = 1}^{n}a_i=\sum_{i = 1}^{n}b_i=\sum_{i = 1}^{n}c_i=tot)
∑i=1nai=∑i=1nbi=∑i=1nci=tot)。你要为他们三个各分配一段连续的区间
[
l
,
r
]
[l,r]
[l,r]中的蛋糕,要求三人分配到区间不相交,不重叠,并且每个人获得的区间中的蛋糕对Ta来说的价值总和大于等于
⌈
t
o
t
3
⌉
\lceil\frac{tot}{3}\rceil
⌈3tot⌉。输出区间时打印出每个人的区间的左右端点下标(下标从1开始),两个下标之间用一个空格隔开。如果没有符合要求的区间,输出-1
。
思路
(这里假设区间的顺序就是
a
,
b
,
c
a,b,c
a,b,c,在写程序时要考虑全排列,一共6种)设三个区间是
[
l
1
,
r
1
]
[l_1,r_1]
[l1,r1],
[
l
2
,
r
2
]
[l_2,r_2]
[l2,r2],
[
l
3
,
r
3
]
(
r
1
<
l
2
,
r
2
<
l
3
)
[l_3,r_3](r_1<l_2,r_2<l_3)
[l3,r3](r1<l2,r2<l3)。由于每个数都非负,区间越大和就越大,也更可能满足和大于等于
⌈
t
o
t
3
⌉
\lceil\frac{tot}{3}\rceil
⌈3tot⌉的要求,
l
1
=
1
l_1=1
l1=1,
l
2
=
r
1
+
1
l_2=r_1+1
l2=r1+1,
l
3
=
r
2
+
1
l_3=r_2+1
l3=r2+1,
r
3
=
n
r_3=n
r3=n显然最优。如果
r
1
=
x
r_1=x
r1=x时
∑
i
=
1
x
a
i
\sum_{i = 1}^{x}a_i
∑i=1xai已经达到要求,则没必要再扩大第一个区间,第三个区间的
l
3
l_3
l3同理,剩下的则全部分配给第二个区间。
有了这样一个贪心思路,就可以开始实现了。用
s
u
m
i
j
sum_{ij}
sumij记录第
i
i
i个人的蛋糕价值的前缀和,从前往后遍历,记录符合要求(即前缀和大于等于
⌈
t
o
t
3
⌉
\lceil\frac{tot}{3}\rceil
⌈3tot⌉)的最小下标
p
s
1
i
ps1_i
ps1i,从后往前遍历,记录符合要求(即后缀和大于等于
⌈
t
o
t
3
⌉
\lceil\frac{tot}{3}\rceil
⌈3tot⌉)的最大下标
p
s
2
i
ps2_i
ps2i。接着枚举每一种排列三个小朋友的方法(
i
d
i
id_i
idi记录第
i
i
i个是谁,
i
i
i从0开始),判断中间的那个人获得的蛋糕价值之和是否符合要求(区间为
[
p
s
1
i
d
0
+
1
,
p
s
2
i
d
2
−
1
]
[ps1_{id_{0}}+1,ps2_{id_{2}}-1]
[ps1id0+1,ps2id2−1]),若符合要求,即可输出。
经验与教训
- 十年OI一场空,不开
long long
见祖宗 -
s
u
m
sum
sum数组千万别用
memset
清空,会TLE,只用将第零个赋值成0就行 - 最好用循环,不要把三个人的值分开来处理,会很烦。全排列时也最好用数组记录编号,用自带的
next_permutation
枚举全排列。 - 输出的时候别忘了还原顺序,依次输出Alice,Bob和Charlie的区间(代码中使用了 a n s [ i ] . p s ans[i].ps ans[i].ps记录原本的编号,输出前按 p s ps ps排序)
代码
#include<bits/stdc++.h>
using namespace std;
int t,n,a[4][200005],ps1[4],ps2[4];
long long tot,sum[4][200005];
struct node{
int ps,x,y;
} ans[3];
bool cmp(node aa,node bb){ return aa.ps<bb.ps; }
int main(){
cin>>t;
while(t--){
sum[1][0]=sum[2][0]=sum[3][0]=tot=0;
cin>>n;
for(int i=1;i<=3;i++)
for(int j=1;j<=n;j++){
cin>>a[i][j];
if(i==1) tot+=a[i][j];
sum[i][j]=sum[i][j-1]+a[i][j];
}
tot=(tot+2)/3;
for(int i=1;i<=3;i++){
for(int j=1;j<=n;j++)
if(sum[i][j]>=tot){
ps1[i]=j;
break;
}
for(int j=n-1;j>=0;j--)
if(sum[i][n]-sum[i][j]>=tot){
ps2[i]=j+1;
break;
}
}
int id[3]={1,2,3};
bool flag=0;
do{
for(int i=0;i<3;i++)
ans[i].ps=id[i];
ans[0].x=1,ans[0].y=ps1[id[0]];
ans[1].x=ps1[id[0]]+1,ans[1].y=ps2[id[2]]-1;
if(sum[id[1]][ans[1].y]-sum[id[1]][ans[1].x-1]<tot)
continue;
ans[2].x=ps2[id[2]],ans[2].y=n;
sort(ans,ans+3,cmp);
for(int i=0;i<3;i++)
cout<<ans[i].x<<' '<<ans[i].y<<' ';
cout<<endl;
flag=1;
break;
}while(next_permutation(id,id+3));
if(!flag) cout<<-1<<endl;
}
return 0;
}