CF949D Curfew
大致题意:现在寝室要熄灯了,宿管开始查寝,但是许多同学们还待在朋友的寝室里。现在已知这一层楼有
n
个寝室排成一排,每个寝室住有
b
个人,而现在每个寝室分别有
a1,a2,a3,…,an
个人,1号宿管从1号寝室往
n
号寝室方向查各个寝室人数,2号宿管从
n
号寝室往1号寝室查各个寝室人数,两个宿管各自负责检查一半的寝室里的人数,当
n
为奇数的时候,1号宿管负责
(n+1)/2
号寝室。由于宿管想快点检查完寝室,他们只检查每个寝室里的人数是否是
b
个人,而不管是哪
b
个人。而机智的同学们可以趁着宿管检查某一个寝室的时候躲起来避免被宿管计入到该寝室的人数,也可以移动
d
个寝室的距离,到另一个寝室里。但是宿管会在检查完一个寝室后把该寝室锁住,里面的人出不来,外面的人也进不去。假设1号宿管检查出有
x
个寝室里的人数不等于
b
,2号宿管检查出有
y
个寝室里的人数不等于
b
,则第二天老师的生气程度和
max(x,y)
成正相关,所以请问最好情况下,
max(x,y)
的最小值是多少。
解法:考虑只有1号宿管的情况下,对于该宿管即将检查的
i
号寝室,如果无论如何都无法使得这个寝室的人数不少于b个人,即
∑i∗(d+1)j=iaj<b
,则不如让这个寝室全部移动到
i+1
号寝室里,让后面的寝室人数更加容易达到b,即使得后续局面更优。
反之,如果可以通过移动使得第
i
个寝室的人数达到b,即
∑i∗(d+1)j=iaj≥b
,那么与其让这
b
个人聚集在后面的寝室,不如直接聚集到
i
号寝室来得到一个合格的寝室。
具体做法是,先求一个数组
a
的前缀和数组
sum
,同时维护一个
res
余量,表示从前面的寝室移动来的人数减去前面的合格寝室里的人的数量,那么贪心策略下当前
i
号寝室合格的充要条件就是
sum[min(n,i∗(d+1))]−sum[i−1]+res≥b
,然后只需要O(n)的时间复杂度就可以统计出合格的寝室数量。
那么如果有两个宿管的时候呢?首先有个显然的结论,如果让前
(n+1)/2
个寝室执行上述贪心策略,则可以使得1号宿管检查的合格寝室数量达到最多,对于2号宿管也也有一样的结论。那么考虑最优化下的结果,就是使得
max(x,y)
取得最小值时的策略,不妨假设
x>y
,那么对于前
(n+1)/2
个寝室,如果通过上述贪心策略来使前
(n+1)/2
个寝室的合格寝室数量增加1,那么可能会对后
n/2
个寝室产生影响,然而最坏只会使后
n/2
个寝室里合格的寝室数量减一,并不会使
max(x,y)
变得更差,于是
max(x,y)
此时就是前
(n+1)/2
个寝室的贪心策略下的不合格数量。
同样的在
x<y
的情况下也有相似的结论。
于是最终的答案就是
max(前(n+1)/2个寝室的贪心策略下的不合格数量,后n/2个寝室的贪心策略下的不合格数量)
代码如下:
#include<bits/stdc++.h>
#define MAXN 510000
#define MAXM 1020000
using namespace std;
typedef long long LL;
inline void read(int &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void read(long long &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void write(long long x){
static const int maxlen=100;
static char s[maxlen];
if (x<0) { putchar('-'); x=-x;}
if(!x){ putchar('0'); return; }
int len=0; for(;x;x/=10) s[len++]=x % 10+'0';
for(int i=len-1;i>=0;--i) putchar(s[i]);
}
int n,len,aim;
int a[ MAXN ];
int tmp[ MAXN ];
int pre[ MAXN ];
int main(){
read(n); read(len); read(aim);
for (int i=1;i<=n;i++)
{
read(a[i]);
tmp[i]=a[i];
a[i]+=a[i-1];
}
int res=0;
int ans=0;
for (int i=1;i<=(n+1)/2;i++)
{
if ( a[min(n*1ll, 1ll*i*(len+1))]-a[i-1]+res>= aim )
{
res-=aim;
ans++;
}
res+=a[i]-a[i-1];
}
int MAX=(n+1)/2-ans;
res=0;
reverse(tmp+1,tmp+n+1);
for(int i=1;i<=n;i++)
a[i]=tmp[i],a[i]+=a[i-1];
ans=0;
for (int i=1;i<=n/2;i++)
{
if ( a[min(n*1ll, 1ll*i*(len+1))]-a[i-1]+res>= aim )
{
res-=aim;
ans++;
}
res+=a[i]-a[i-1];
}
MAX=max(MAX , n/2-ans);
printf("%d\n",MAX);
return 0;
}