P11234 [CSP-S 2024] 擂台游戏

看看了看今年的csps,前三题一眼就秒了,这最后一题想了挺久,还写了快两个小时,要是在正式赛场上估计是要暴毙了,不过好在我已经退役了,希望参赛的选手能有好的发挥

题目大意

太长了,不写了

题解

考虑每次加入一个人,然后分析变化的答案
经过一些分析,可以发现一些性质
1.对于完全没有确定能力值选手的比赛,里面的每个编号都有机会获得胜利
2.没有确定能力值的选手,有机会获得胜利的,一定是结尾是最大编号的连续的一段
这样我们就可以通过计算出一个\(\text{maxn}\),表示最大的没有确定能力值的选手且没可能取得胜利的编号,然后用一个等差数列求和,就能求出没有确定能力值的选手的贡献
每个比赛会在不断加入选手的过程中确定胜者,对于一个确定胜者的比赛,显然\(\text{maxn}\)一定大于等于这个比赛覆盖到的最大编号,\(\text{maxn}\)就是所有确定胜者的比赛覆盖的编号最大值

然后考虑怎么计算已经确定能力值的选手的贡献
对于每个选手,可以计算出一个数组\(lim\)表示,这个选手向上走,遇到的最大的考验,如果\(a_i<lim_i\),说明这个选手肯定不能胜利
考虑现在加入一个确定能力值的选手\(i\),如果\(a_i\ge lim_i\),就先把它加进贡献里
然后向上走,看看上面有哪些比赛确定胜者之后结果是它,这部分可以通过一些分类讨论完成,如果遇到不能确定胜者的,就可以直接退出了,在这个过程中要把一些以前可能是胜者的选手而现在不可能的选手的贡献去掉
需要注意一种情况,就是此时这个选手是遇到的比赛中编号较大的,而这个比赛的胜者是由这个选手决定的,而它没有获胜,这时候这个比赛的胜者将会确定为编号较小的那个选手,这时候要把目前考虑的选手换成编号较小的那个选手,然后继续向上确定比赛的胜者
由于每个比赛只能有一个确定的胜者,所以这个算法的时间复杂度是\(O(n)\)的,然后更新\(lim\)的时候
每次只用更新一半,所以时间复杂度也是\(O(n)\)

代码

点击查看代码
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=2e5+100;
int n,m,b[N+10],a[N+10],t[4];
struct qst
{
	int id,c;
}p[N+10];
bool cmp(qst a,qst b){return a.c<b.c;}
char r[21][N+10];
int f[21][N+10],lim[N+10];
bool vis[N+10];
void pushdown(int k,int u,int maxn)
{
	if(k==0)
	{
		lim[u]=maxn;
		return;
	}
	if(r[k][u]=='0')
	{
		pushdown(k-1,u*2-1,max(maxn,k));
		pushdown(k-1,u*2,maxn);
	}
	else
	{
		pushdown(k-1,u*2-1,maxn);
		pushdown(k-1,u*2,max(maxn,k));
	}
}
int main()
{
//	freopen("arena5.in","r",stdin);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&b[i]);
	for(int i=1;i<=m;i++)
	{
		p[i].id=i;
		scanf("%d",&p[i].c);
	}
	sort(p+1,p+1+m,cmp);
	int k=0;
	while((1<<k)<n) k++;
	for(int i=1;i<=k;i++)
		scanf("%s",r[i]+1);
	int T;
	scanf("%d",&T);
	for(;T--;)
	{
		for(int i=0;i<4;i++)
			scanf("%d",&t[i]);
		for(int i=1;i<=n;i++)
			a[i]=b[i]^t[i%4];
		for(int j=1;j<=(1<<k);j++)
			f[0][j]=j,lim[j]=0,vis[j]=false;
		for(int i=1;i<=k;i++)
		{
			for(int j=1;j<=(1<<k-i);j++)
			{
				if(r[i][j]=='0')
				{
					if(a[f[i-1][j*2-1]]>=i)
						f[i][j]=f[i-1][j*2-1];
					else
						f[i][j]=f[i-1][j*2];
				}
				else
				{
					if(a[f[i-1][j*2]]>=i)
						f[i][j]=f[i-1][j*2];
					else
						f[i][j]=f[i-1][j*2-1];
				}
			}
		}
		ll Ans=0,ans=0;
		for(int i=1,k=0,maxn=0,j=1;i<=n;i++)
		{
			while((1<<k)<i)
			{
				k++;
				if(r[k][1]=='0')
				{
					if(a[f[k-1][1]]>=k)
						maxn=max(maxn,(1<<k));
					else
					{
						if(vis[f[k-1][1]])
						{
							vis[f[k-1][1]]=false;
							Ans-=f[k-1][1];
						}
					}
					pushdown(k-1,2,0);
				}
				else
					pushdown(k-1,2,k);
			}
			if(i<=maxn)
			{
				while(j<=m&&p[j].c<=i)
				{
					ans^=(p[j].id*(Ans+1ll*((1<<k)-maxn)*((1<<k)+maxn+1)/2));
					j++;
				}
				continue;
			}
			if(a[i]>=lim[i])
			{
				Ans+=i;
				vis[i]=true;
			}
			maxn=i;
			int u=i,ri=i;
			for(int rk=1;rk<=k;rk++)
			{
				int fa=(u+1>>1);
				if(u==fa*2)
				{
					if(r[rk][fa]=='1')
					{
						if(a[ri]>=rk)
						{
							if(vis[f[rk-1][fa*2-1]])
							{
								vis[f[rk-1][fa*2-1]]=false;
								Ans-=f[rk-1][fa*2-1];
							}
							maxn=max(maxn,fa<<rk);
						}
						else
							ri=f[rk-1][fa*2-1];
					}
					else
					{
						if(a[f[rk-1][fa*2-1]]>=rk)
							break;
					}
					maxn=max(maxn,fa<<rk);
				}
				else
				{
					if(r[rk][fa]=='0')
					{
						if(a[ri]>=rk)
							maxn=max(maxn,fa<<rk);
						else
							break;
					}
					else break;
				}
				u=fa;
			}
			while(j<=m&&p[j].c<=i)
			{
				ans^=(p[j].id*(Ans+1ll*((1<<k)-maxn)*((1<<k)+maxn+1)/2)); 
				j++;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2024-10-29 15:17  _doctorZ  阅读(103)  评论(2编辑  收藏  举报