[bzoj3353]Archery

tips1:下标相关均默认在[1,n]的环上

tips2:可能需要大量的感性理解(后面我也已经神智不清了)


设排名为x,将x的编号为0>x的编号为1

di为位置i上两人的编号和[i>1],则每次从i移动到i1的人编号为[di1]

在此基础上,不断找到二元组(i,j)满足以下两种情况之一:

  • di>0,dj<0di1=...=dj+1=0

  • i=1,d1=2j=minj2,dj0j(若不存在即所有人编号均为1

此时,经过n轮比赛会使得di1,dj+1,且不影响其余位置,不妨在初始处理

当不存在(i,j)时,di均同号(包含0)且|di|1,进而每轮比赛即旋转(0)或不变(0)

关于如何找(i,j),维护两个栈st+st,并逆序遍历[1,n]

  • di>0,则在st+中加入dii
  • di<0,则在st+中弹出di个元素(与i配对),不足则在st中加入等量个i

最终,不断将st+的栈顶和st的栈底配对,并特判d1=2的情况

回到原问题,枚举x所在位置k,对dk是否+1分别求出最终的d序列

两者恰有一个位置不同,且该位置即为x的最终位置,时间复杂度为O(n2)


特判x=1,显然最终答案为n(此时将dk+1后会使得所有人编号均为1

最终di0di0取决于i=1ndi=(2nx)(n1)<0,化简得x>n+1

在此基础上,仅需求出"不存在(i,j)时"两者不同的位置,最终位置即该位置R或不变

对于第一类中的(i,j),若k[j,i),则将dk+1后仍满足其性质

将遍历顺序改为k,k1,...,k+1,则过程中选择的(i,j)均满足k[j,i)

在此基础上,两者不同的位置有以下三种情况:

  • x>n+1,则|st+|<|st|

    dk+1后,k会与st栈顶底匹配,其余元素顺次向后匹配

    最终,不同的位置即st栈底的第|st+|+1个元素(额外匹配)

  • xn+1st为空,则仅需考虑d1=2的情况

    kminj2,dj0j,则不同的位置即k,否则即minj>k,dj0j

    注意到后者与k无关(即mini1,ai<mi2+1),可以直接预处理

  • xn+1st不为空,类似第一种,不同的位置即st+栈顶第|st|个元素(取消匹配)

    另外,这总不会影响d1=2时的j,这可以分类讨论:

    • minj2,dj0j<k,即两次均选该位置
    • minj2,dj0jk,由于d1=2且最终st非空,必然已经匹配
#include<bits/stdc++.h>
using namespace std;
const int N=400005;
int n,m,R,pos,ans,a[N];
vector<int>v,V;//st+和st-
int query(int k){
	v.clear(),V.clear();
	for(int i=k;i;i--){
		if (a[(i<<1)-1])v.push_back(i);
		if ((i<k)&&(a[i<<1]))v.push_back(i);
		if (i>1){
			if (v.empty())V.push_back(i);
			else v.pop_back();
		}
	}
	for(int i=n;i>k;i--){
		if (a[(i<<1)-1])v.push_back(i);
		if (a[(i<<1)-2])v.push_back(i);
		if (v.empty())V.push_back(i);
		else v.pop_back();
	}
	if (m>n+1)return V[v.size()];
	if (!V.empty())k=v[v.size()-V.size()];
	else{
		for(int i=1;i<(n<<1);i++)
			if (!a[i]){
				if (i>=(k<<1))k=(i>>1)+1;
				break;
			}
	}
	return (k-R%n+n-1)%n+1;
}
int main(){
	scanf("%d%d%d",&n,&R,&m);
	if (m==1){
		printf("%d\n",n);
		return 0;
	}
	for(int i=1;i<(n<<1);i++){
		scanf("%d",&a[i]);
		a[i]=(a[i]<m ? 0 : 1);
	}
	ans=n+1;
	for(int i=n;i;i--){
		int s=query(i);
		if (s<ans)pos=i,ans=s;
	}
	printf("%d\n",pos);
	return 0;
}

考虑分别处理出逆序遍历[1,k](k,n]后的结果,即可O(1)实现query,即

if (svp[k]<sVs[k+1]){
	if (m>n+1){
		if (svs[k+1]<sVp[k])return Vp[k][sVp[k]-svs[k+1]-1];
		return Vs[k+1][svs[k+1]-sVp[k]+svp[k]];
	}
	return (vs[k+1][svs[k+1]-(sVp[k]+sVs[k+1]-svp[k])]-R%n+n-1)%n+1;
}
else{
	if (m>n+1)return Vp[k][sVp[k]-(svp[k]-sVs[k+1]+svs[k+1])-1];
	if (sVp[k]){
		int s=svp[k]-sVs[k+1]+svs[k+1]-sVp[k];
		if (s<svp[k]-sVs[k+1])k=vp[k][svp[k]-s-1];
		else k=vs[k+1][s-(svp[k]-sVs[k+1])];
	}
	else{
		if ((k<<1)<=mn)k=(mn>>1)+1;
	}
	return (k-R%n+n-1)%n+1;
}

(其中vp/Vp[k]表示[1,k]v/V翻转后,vs/Vs[k+1]表示(k,n]v/Vs表示的大小)

关于vp/Vp,分类讨论后,即

for(int i=2;i<=n;i++){		
	vp[i]=vp[i-1],Vp[i]=Vp[i-1];
	if (a[(i<<1)-2]){
		if (Vp[i].empty())vp[i].push_back(i-1);
		else Vp[i].pop_back();
	}
	if (!a[(i<<1)-1])Vp[i].push_back(i);
}

关于vs/Vs,分类讨论后,即

for(int i=n;i;i--){
	vs[i]=vs[i+1],Vs[i]=Vs[i+1];
	if (a[(i<<1)-1])vs[i].push_back(i);
	if (a[(i<<1)-2])vs[i].push_back(i);
	if (vs[i].empty())Vs[i].push_back(i);
	else vs[i].pop_back();
}

逆序(指[1,n])做一遍,存储每次vs[i].pop_back()的数后即可撤销

由于复杂度瓶颈在于拷贝,此时即可在线做,时间复杂度为O(n)

#include<bits/stdc++.h>
using namespace std;
const int N=400005;
int n,m,R,mn,svp,sVp,svs,sVs,pos,ans,a[N],z[N];
vector<int>vp,Vp,vs,Vs;
int query(int k){
	if (svp<sVs){
		if (m>n+1){
			if (svs<sVp)return Vp[sVp-svs-1];
			return Vs[svs-sVp+svp];
		}
		return (vs[svs-(sVp+sVs-svp)]-R%n+n-1)%n+1;
	}
	else{
		if (m>n+1)return Vp[sVp-(svp-sVs+svs)-1];
		if (sVp){
			int s=svp-sVs+svs-sVp;
			if (s<svp-sVs)k=vp[svp-s-1];
			else k=vs[s-(svp-sVs)];
		}
		else{
			if ((k<<1)<=mn)k=(mn>>1)+1;
		}
		return (k-R%n+n-1)%n+1;
	}
	return 0;
}
int main(){
	scanf("%d%d%d",&n,&R,&m);
	if (m==1){
		printf("%d\n",n);
		return 0;
	}
	for(int i=1;i<(n<<1);i++){
		scanf("%d",&a[i]);
		a[i]=(a[i]<m ? 0 : 1);
	}
	for(int i=1;i<(n<<1);i++)
		if (!a[i]){mn=i;break;}
	for(int i=n;i;i--){
		if (a[(i<<1)-1])svs++,vs.push_back(i);
		if (a[(i<<1)-2])svs++,vs.push_back(i);
		z[i]=(svs ? vs.back() : 0);
		if (!svs)sVs++,Vs.push_back(i);
		else svs--,vs.pop_back();
	}
	ans=n+1;
	for(int i=1;i<=n;i++){
		if (i==1){
			if (a[1])svp++,vp.push_back(1);
		}
		else{
			if (a[(i<<1)-2]){
				if (!sVp)svp++,vp.push_back(i-1);
				else sVp--,Vp.pop_back();
			}
			if (!a[(i<<1)-1])sVp++,Vp.push_back(i);
		}
		if (z[i])svs++,vs.push_back(z[i]);
		else sVs--,Vs.pop_back();
		if (a[(i<<1)-1])svs--,vs.pop_back();
		if (a[(i<<1)-2])svs--,vs.pop_back();
		int s=query(i);
		if (s<=ans)pos=i,ans=s;
	}
	printf("%d\n",pos);
	return 0;
}
posted @   PYWBKTDA  阅读(149)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2022-03-02 [uoj671]诡异操作
2021-03-02 [cf1491G]Switch and Flip
2021-03-02 [cf1491F]Magnets
点击右上角即可分享
微信分享提示