P14462 【MX-S10-T3】『FeOI-4』寻宝游戏

P14462 【MX-S10-T3】『FeOI-4』寻宝游戏

P14462 【MX-S10-T3】『FeOI-4』寻宝游戏 - 洛谷 (luogu.com.cn)

分类讨论。

  • \(len\ge 3\)

    找到一个目标桶 \(x\),把剩下的都扔进去。

    设剩下的桶之中,个数和为 \(sum\),最大的有 \(mx\) 个。

    • \(sum\) 为偶数。

      可以直接向 \(x\) 里扔。一个下界为 \(\max(sum/2,mx)\),考虑构造出这个下界:

      • \(sum/2\ge mx\),每次拿出最大和次大的向 \(x\) 扔,一定可行。
      • 否则,先不断地拿 \(mx\) 和其他一个桶向一个空桶扔,直到 \(sum/2\ge mx\),需要 \(mx\) 次操作。
    • \(sum\) 为奇数。

      • \(x\) 不是空桶。

        先拿出 \(x\)\(mx\) 向空桶扔,然后变为上面的问题。

        \(x\) 剩下的桶中次大值为 \(se\)

        • \(mx=se\),答案为 \(\max((sum+1)/2,mx,2)+1\)
        • 否则,答案为 \(\max((sum+1)/2,mx-1,2)+1\)
      • \(x\) 是空桶

        讨论起来就麻烦很多,但答案只会是 \(\max((sum-1)/2,mx-d,2)+2\),其中 \(d\in \{0,1,2\}\)

        无论 \(d\) 取何值,都不比选一个非空桶优,所以不需要考虑。

  • \(len=1\),平凡。

  • \(len=2\)

    设两个桶里有 \(x,y\) 个,不妨设 \(x\le y\)

    首先只有两个桶是没办法做的,需要一个空桶转换为 \(len\ge 3\) 的情况。

    • \(x=1,y=1\),答案为 \(1\)
    • \(x=1,y=2\),无解。
    • \(x>1\),用一步操作变为 \(x-1,y-1,2\)
    • \(x=1\),用两步操作变为 \(2,y-2,1\)
    • \(x+y\) 为偶数,答案还要考虑 \(\max((x+y)/2,x,y)\)

对于 \(len\ge 3\) 的情况,只有区间最大值和严格次大值有可能成为目标。st 表或线段树均可。

int n,m,a[N];
ll s[N];

struct Info{
	int f,pf,g,pg,h; //最大值,出现位置,严格次大值,出现位置,非严格次大值
	
	friend Info operator + (Info x,Info y){
		Info z;
		if(x.f==y.f){
			z.f=x.f,z.pf=x.pf;
			if(x.g>=y.g) z.g=x.g,z.pg=x.pg;
			else z.g=y.g,z.pg=y.pg;
			z.h=y.f;
		}
		else if(x.f>y.f){
			z.f=x.f,z.pf=x.pf;
			if(x.g>=y.f) z.g=x.g,z.pg=x.pg;
			else z.g=y.f,z.pg=y.pf;
			z.h=max(x.h,y.f);
		}
		else{
			z.f=y.f,z.pf=y.pf;
			if(y.g>=x.f) z.g=y.g,z.pg=y.pg;
			else z.g=x.f,z.pg=x.pf;
			z.h=max(y.h,x.f);
		}
		return z;
	} 
}tr[N<<2];

void Buildtr(int p,int l,int r){
	if(l==r){
		tr[p].f=a[l],tr[p].h=-IINF;
		tr[p].pf=l,tr[p].g=-IINF,tr[p].pg=-1;
		return;
	}
	int mid=(l+r)>>1;
	Buildtr(p<<1,l,mid),Buildtr(p<<1|1,mid+1,r);
	tr[p]=tr[p<<1]+tr[p<<1|1]; 
}

Info Ask(int p,int l,int r,int L,int R){
	if(L>R) return Info{-IINF,-1,-IINF,-1,-IINF};
	if(L<=l&&r<=R) return tr[p];
	int mid=(l+r)>>1;
	if(L<=mid&&mid<R)
		return Ask(p<<1,l,mid,L,R)+Ask(p<<1|1,mid+1,r,L,R);
	else if(L<=mid) return Ask(p<<1,l,mid,L,R);
	else return Ask(p<<1|1,mid+1,r,L,R);
} 

ll Calc(int x,int y){
	if((x+y)%2==0) return max((x+y)>>1,max(x,y));
	else{
		int mx=max(x,y),se=min(x,y);
		if(mx==se) return max((x+y+1)>>1,max(mx,2))+1;
		else return max((x+y+1)>>1,max(mx-1,2))+1;
	}
}

ll Work0(int l,int r){
	if(a[l]==1&&a[r]==1) return 1;
	else if(a[l]==1&&a[r]==2) return -1;
	else if(a[l]==2&&a[r]==1) return -1;
	ll A=a[l],B=a[r],C=0,ans=0,res=LINF;
	if(A>B) swap(A,B);
	if(A==1) A=2,B-=2,C=1,ans=2;
	else --A,--B,C=2,ans=1;
	if((A+B+C)%2==0)
		Ckmin(res,max((A+B+C)>>1,max({A,B,C})));
	Ckmin(res,Calc(A,B));
	Ckmin(res,Calc(A,C));
	Ckmin(res,Calc(B,C));
	ans+=res; return ans;
}

ll Work1(int l,int r){
	ll mx,se,sum,ans=LINF;
	Info all=Ask(1,1,n,l,r),res;
	int P=all.pf,Q=all.pg;
	if((s[r]-s[l-1])%2==0) Ckmin(ans,max((s[r]-s[l-1])>>1,(ll)all.f));
	res=Ask(1,1,n,l,P-1)+Ask(1,1,n,P+1,r);
	mx=res.f,se=res.h,sum=s[r]-s[l-1]-a[P];
	if(sum%2==0) Ckmin(ans,max(sum>>1,mx));
	else if(mx==se) Ckmin(ans,max((sum+1)>>1,max(mx,2ll))+1);
	else Ckmin(ans,max((sum+1)>>1,max(mx-1,2ll))+1);
	if(Q==-1) return ans;
	res=Ask(1,1,n,l,Q-1)+Ask(1,1,n,Q+1,r);
	mx=res.f,se=res.h,sum=s[r]-s[l-1]-a[Q];
	if(sum%2==0) Ckmin(ans,max(sum>>1,mx));
	else if(mx==se) Ckmin(ans,max((sum+1)>>1,max(mx,2ll))+1);
	else Ckmin(ans,max((sum+1)>>1,max(mx-1,2ll))+1);
	return ans;
}

void Solve(){
	read(n),read(m);
	for(int i=1;i<=n;i++){
		read(a[i]);
		s[i]=s[i-1]+a[i];
	}
	Buildtr(1,1,n);
	while(m--){
		int l,r; read(l),read(r);
		if(l==r) puts("0");
		else if(l+1==r) printf("%lld\n",Work0(l,r));
		else printf("%lld\n",Work1(l,r));
	} 
}

signed main(){
	int T; read(T);
	while(T--) Solve();
	return 0;
}
posted @ 2025-11-09 17:52  XP3301_Pipi  阅读(4)  评论(0)    收藏  举报
Title