[bzoj3353]Archery

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

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


设排名为\(x\),将\(\le x\)的编号为\(0\)\(>x\)的编号为\(1\)

\(d_{i}\)为位置\(i\)上两人的编号和\(-[i>1]\),则每次从\(i\)移动到\(i-1\)的人编号为\([d_{i}\ge 1]\)

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

  • \(d_{i}>0,d_{j}<0\)\(d_{i-1}=...=d_{j+1}=0\)

  • \(i=1,d_{1}=2\)\(j=\min_{j\ge 2,d_{j}\le 0}j\)(若不存在即所有人编号均为\(1\)

此时,经过\(\le n\)轮比赛会使得\(d_{i}-1,d_{j}+1\),且不影响其余位置,不妨在初始处理

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

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

  • \(d_{i}>0\),则在\(st^{+}\)中加入\(d_{i}\)\(i\)
  • \(d_{i}<0\),则在\(st^{+}\)中弹出\(-d_{i}\)个元素(与\(i\)配对),不足则在\(st^{-}\)中加入等量个\(i\)

最终,不断将\(st^{+}\)的栈顶和\(st^{-}\)的栈底配对,并特判\(d_{1}=2\)的情况

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

两者恰有一个位置不同,且该位置即为\(x\)的最终位置,时间复杂度为\(O(n^{2})\)


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

最终\(d_{i}\ge 0\)\(d_{i}\le 0\)取决于\(\sum_{i=1}^{n}d_{i}=(2n-x)-(n-1)<0\),化简得\(x>n+1\)

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

对于第一类中的\((i,j)\),若\(k\not\in [j,i)\),则将\(d_{k}+1\)后仍满足其性质

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

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

  • \(x>n+1\),则\(|st^{+}|<|st^{-}|\)

    \(d_{k}+1\)后,\(k\)会与\(st^{-}\)栈顶底匹配,其余元素顺次向后匹配

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

  • \(x\le n+1\)\(st^{-}\)为空,则仅需考虑\(d_{1}=2\)的情况

    \(k\ne \min_{j\ge 2,d_{j}\le 0}j\),则不同的位置即\(k\),否则即\(\min_{j>k,d_{j}\le 0}j\)

    注意到后者与\(k\)无关(即\(\min_{i\ge 1,a_{i}<m}\lfloor\frac{i}{2}\rfloor+1\)),可以直接预处理

  • \(x\le n+1\)\(st^{-}\)不为空,类似第一种,不同的位置即\(st^{+}\)栈顶第\(|st^{-}|\)个元素(取消匹配)

    另外,这总不会影响\(d_{1}=2\)时的\(j\),这可以分类讨论:

    • \(\min_{j\ge 2,d_{j}\le 0}j<k\),即两次均选该位置
    • \(\min_{j\ge 2,d_{j}\le 0}j\ge k\),由于\(d_{1}=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/V\)\(s*\)表示\(*\)的大小)

关于\(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 @ 2023-03-02 21:43  PYWBKTDA  阅读(146)  评论(0编辑  收藏  举报