Loading

CF2070E(edu.175) Game with Binary String 题解

题意:

题目描述较为繁琐,vj上有翻译版:点这里

思路:

首先可以确定的是,如果玩家 \(2\) 的策略是最优策略,那么其一定会选形如 \(01/10\) 这样的串。因为玩家 \(2\) 必须要选至少一个 \(1\) ,那么一定我们希望每次尽量少用 \(1\) 并且多消耗玩家1需要的 \(0\),这样才可以使得可以撑更多的回合,更优。

又发现,我们如果想让玩家 \(1\) 获胜,那么必然其比玩家 \(2\) 多玩了 \(1\) 轮。考虑玩家 \(2\) 输了最后会出现怎样的局面:

显然有两种情况。第一种是所有 \(1\) 都被拿完了,并且局面还能保证玩家 \(1\) 至少还能玩 \(1\) 轮,第二种是最后还剩一个 \(1\) ,但是没有其他任何元素了,那么此时玩家 \(2\) 也无法胜利。

所以我们考虑记 \(cnt_{0/1}\) 表示序列中 \(0/1\) 的个数,先考虑上述第一种情况,此时玩家 \(2\) 拿完了所有 \(1\) ,并且按照最优策略。那么玩家 \(2\) 就拿了 \(cnt_1\)\(1\)\(0\),此时若要满足玩家 \(1\) 能至少比玩家 \(2\) 多玩一轮,则一定,其拿了 \(2\times(cnt_1 + 1)\)\(0\)。也就等价说明了,要满足玩家 \(1\) 必胜,一定要满足:

\[cnt_0\geq3\times cnt_1+2 \]

什么,你说怎么保证每一次玩家 \(1\) 都可以行动?很简单,我们换一个视角思考这个问题,考虑我们现在已经有了 \(cnt_1\)\(1\),我们将所有的 \(0\) 分配到任意的两个 \(1\) 之间,因为至少有 \(3\times cnt_1+2\)\(0\) 则所有的分配情况下都一定会存在至少有 \(3\) 个连续(相邻)的 \(0\) \((cnt_1>0)\) 的方案,所以不用担心不够用,并且每一轮玩家 \(1\) 和玩家 \(2\) 都进行了游戏后,这个不等式是依然成立的,所以每一次都可以保证玩家 \(1\) 可以行动。

又很显然的,当上述不等式取等时,我们的必胜策略已经到极限了,再让 \(cnt_0\) 变小就无法保证玩家 \(1\) 必胜了,所以上述不等式是充要的。

用同样的分析方法,可以得出如果要产生第二种必胜结局,则一定:

\[cnt_0=3\times cnt_1-1 \]

证明思路同上,略。

现在考虑统计答案,我们试着枚举区间左端点 \(r\),那么我们现在需要知道的就是有多少个 \(l\) 是合法的。显然一个合法的 \(l\) 需要满足的充要条件(情况1)是:

\[(cntr_0-cntl_0)\geq3\times(cntr_1-cntl_1) + 2 \]

我们试着对式子进行变换:

\[(cntr_0-3\times cntr_1)-2\geq cntl_0 - 3\times cntl_1 \]

你发不等式现左边括号中的内容与不等式右边是同构的。所以我们定义函数 \(f(i)=cnti_0-3\times cnti_1\) ,所以不等式变为:

\[f(r) - 2\geq f(l) \]

那么我们现在只需要找出这样的 \(l\) ,如果我们暴力枚举的话,复杂度是 \(O(n^2)\) 的,显然无法通过本题,但你发现这是一个求区间 \([1,r-1]\) 中的合法下标的问题,既然是区间问题,我们可以考虑建立一颗树状数组来解决这个问题,以减少对前面区间的重复遍历。但是这不是一般的树状数组。事实上,我们应当建立一颗值域树状数组,也就是下标为 \(f(x)\) 中存的东西。并且每次遍历完 \(r\) 后,都需要将树状数组中 \(f(r)\) 的部分加\(1\),即遍历到了多一个值为 \(f(r)\) 的下标 \(r\) ,方便给后面的数计算。

对于情况二是同理的,只是需要注意的是,情况二是等号,所以需要减去小于 \(f(r) + 1\) 的部分的贡献(这么说有点抽象,直接看代码可能更好理解)。

Code:

注意上述分析中 \(f(x)\) 可能为负数,所以需要加一个偏移量来保证树状数组下标的非负。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long 
inline int read(){
	char c=getchar();bool f=0;int x=0;
	while(c > '9' || c < '0') f|=c=='-',c=getchar();
	while(c >= '0'&&c <= '9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	if(f) x=-x;return x;
}

const int M = 4e6 + 10,N = 3e5 + 10;
char s[N];
int n,tree[M],pre[N],up_limit;
int lowbit(int x){return x & (-x);}
 
void add(int x, int k){
    for(;x <= up_limit;x += lowbit(x)) 
		tree[x] += k;
}

LL query(int x){
    LL res = 0;
    for(;x;x -= lowbit(x)) 
		res += tree[x];
    return res;
}

int main()
{
	n = read(); 
	scanf("%s",s + 1);
	LL ans = 0;
	up_limit = 3e6 + 10;
	for(int i = 1;i <= n;++i) 
	{
		pre[i] = pre[i - 1] + (s[i] == '0' ? 1 : -3); 
	}
	for(int i = 0;i <= n;++i) pre[i] += 3 * n + 5;
	
	add(pre[0],1);
	for(int i = 1;i <= n;++i)
	{
		ans += query(pre[i] - 2) + (query(pre[i] + 1) - query(pre[i]));
		add(pre[i],1);
	}
	
	printf("%lld",ans);
	return 0;
}
posted @ 2025-03-05 08:26  AxB_Thomas  阅读(67)  评论(1)    收藏  举报