• 题解 P4734 【[BalticOI 2015]Hacker】


    分析:

    我们发现假如第一步选择位置 (i) ,那么系统操作者能把 ( ext{Byteasar}) 限制成经过 (i) 的长度为 (lceil dfrac{n}{2} ceil)

    的任意一个序列。那么系统操作者肯定会选择价值最小的那个。

    现在问题就是求出经过每个位置的长度为 (lceil dfrac{n}{2} ceil)

    的序列的价值的最小值,然后每个位置最小值的最大值就是答案。

    现在考虑怎么求出这个最大值。

    我们发现这样的序列一共只有 (n) 个,我们可以对这 (n) 个序列的价值从大到小排个序。

    然后对每个序列的范围做一下区间覆盖。显然当一个位置被覆盖了 (lceil dfrac{n}{2} ceil) 次的时候,当前的序列的价值就是答案。

    区间覆盖线段树就可以了。

    ( ext{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

    线段树虽然能过,但是好慢啊。

    我们发现,用线段树进行操作好像有点多余,其实完全可以只通过前缀和 (+) 二分的方式来维护序列。

    再加一点点优化。。。

    我们的简短的代码就完成力!线段树就是弱欸。

    ( ext{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;
    }
    

    完力。

  • 相关阅读:
    【BZOJ3309】DZY Loves Math(莫比乌斯反演)
    【CF666E】Forensic Examination(后缀自动机,线段树合并)
    【HDU5730】Shell Necklace(多项式运算,分治FFT)
    【洛谷4389】付公主的背包(生成函数,多项式运算)
    【CF528D】Fuzzy Search(FFT)
    【BZOJ4259】残缺的字符串(FFT)
    【BZOJ4503】两个串(FFT)
    【CF954I】Yet Another String Matching Problem(FFT)
    Java中泛型Class<T>、T与Class<?>、 Object类和Class类、 object.getClass()和Object.class
    H5和原生APP之间的区别
  • 原文地址:https://www.cnblogs.com/nakiri-ayame-suki/p/13834729.html
Copyright © 2020-2023  润新知