题解 P4734 【[BalticOI 2015]Hacker】
分析:
我们发现假如第一步选择位置 \(i\) ,那么系统操作者能把 \(\text{Byteasar}\) 限制成经过 \(i\) 的长度为 \(\lceil \dfrac{n}{2} \rceil\) 。
的任意一个序列。那么系统操作者肯定会选择价值最小的那个。
现在问题就是求出经过每个位置的长度为 \(\lceil \dfrac{n}{2} \rceil\) 。
的序列的价值的最小值,然后每个位置最小值的最大值就是答案。
现在考虑怎么求出这个最大值。
我们发现这样的序列一共只有 \(n\) 个,我们可以对这 \(n\) 个序列的价值从大到小排个序。
然后对每个序列的范围做一下区间覆盖。显然当一个位置被覆盖了 \(\lceil \dfrac{n}{2} \rceil\) 次的时候,当前的序列的价值就是答案。
区间覆盖线段树就可以了。
\(\text{code1}:\)
#include"cstdio"
#include"algorithm"
#define M 1000010
namespace IAKIOI{
const int L=1<<20|1;
char buffer[L],*S,*TT;
// #define getchar() ((S==TT&&(TT=(S=buffer)+fread(buffer,1,L,stdin),S==TT))?EOF:*S++)
inline void read(int &x){
x=0;int f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
inline void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
}
using namespace IAKIOI;
int n,a[M],s[M<<2],k,m,ans,la[M<<2];
struct node{
int l,r,v;
}c[M];
bool cmp(node a,node b){
return a.v>b.v;
}
void update(int x){
s[x]=std::max(s[x<<1],s[x<<1|1]);
}
void pushdown(int x){
la[x<<1]+=la[x],la[x<<1|1]+=la[x];
s[x<<1]+=la[x],s[x<<1|1]+=la[x];
la[x]=0;
}
inline void change(int x,int l,int r,int a,int b){
if(a<=l&&b>=r)return s[x]++,la[x]++,void();
int mid=(l+r)>>1;if(la[x])pushdown(x);
if(a<=mid)change(x<<1,l,mid,a,b);
if(b>mid)change(x<<1|1,mid+1,r,a,b);
update(x);
}
int main(){
read(n);
m=n;n<<=1;k=(m+1)/2;
for(register int i=1;i<=m;i++)
read(a[i]),a[i+m]=a[i];
for(register int i=1;i<=n;i++)
a[i]+=a[i-1];
for(register int i=1;i<=m;i++)
c[i]=node{i,i+k-1,a[i+k-1]-a[i-1]};
std::sort(c+1,c+n+1,cmp);
for(register int i=1;i<=m;i++){
if(c[i].r<=m)
change(1,1,m,c[i].l,c[i].r);
else change(1,1,m,c[i].l,m),change(1,1,m,1,c[i].r-m);
if(s[1]==k)
return write(c[i].v),0;
}
}
BUTT!
线段树虽然能过,但是好慢啊。
我们发现,用线段树进行操作好像有点多余,其实完全可以只通过前缀和 \(+\) 二分的方式来维护序列。
再加一点点优化。。。
我们的简短的代码就完成力!线段树就是弱欸。
\(\text{code2}:\)
#include"cstdio"
#include"map"
#define M 500050
int T[M],a[M],a1[M],a2[M],n,len,ans;
namespace IAKIOI{
const int L=1<<20|1;
char buffer[L],*S,*TT;
#define getchar() ((S==TT&&(TT=(S=buffer)+fread(buffer,1,L,stdin),S==TT))?EOF:*S++)
inline void read(int &x){
x=0;int f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
inline void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
}
using namespace IAKIOI;
int main(){
read(n),len=n+1>>1;
for(int i=0;i<n;++i)
read(a[i]),a1[i]=(i?a1[i-1]:0)+a[i];
for(int i=0;i+len-1<n;++i)
a2[i]=a1[i+len-1]-(i?a1[i-1]:0);
for(int i=n-len+1;i<n;++i)
a2[i]=a1[n-1]-a1[i-1]+a1[len-n+i-1];
int con=std::max(n-1>>1,0),L=1,R=0;
for(int i=n-con;i<n;T[++R]=i++)
while(L<=R&&a2[T[R]]>=a2[i])--R;
for(int i=0;i<n;++i){
while(L<=R&&(i-T[L]+n)%n>con)++L;
while(L<=R&&a2[T[R]]>=a2[i])--R;
T[++R]=i,ans=std::max(ans,a2[T[L]]);
}
return write(ans),0;
}
完力。