欢迎来我的博客看这篇题解

Problem

在两人竞技比赛中,对于任何正整数 \(a\) ,我们定义 \(BO(2 a-1)\) 如下:两名玩家继续竞争,直到其中一人获胜 \(a\) 次,那么他赢得整个比赛。\(BO(2 a-1)\) 最多包含 \(2a-1\) 小局游戏,最少包含 \(a\) 小局游戏。

现在两个人进行一场 DotA2 比赛,使用的是 \(BO(2b-1)\ \texttt{of}\ BO(2a-1)\) 赛制。该赛制由最多 \(2b-1\) 最少 \(b\) 场主要比赛组成,每个主要比赛都是一个 \(BO(2a-1)\),由最多 \(2a-1\) 最少 \(a\) 局次要比赛组成。

假如比赛的结果是预先确定的:有一个长度为 \(n\) 的 \(0-1\) 串 \(T\) ,其中 1 表示 A 获胜,0 表示 B 获胜。A 和 B 的每一局次要游戏结果都从串 \(T\) 中获取。假如从 \(T\) 串的每个位置开始重复获取次要游戏结果,求最后谁赢了?

\(1\le n,a,b\le 10^5\)

Example

7 2 2
1010101

在该样例中,每个主要比赛都是 \(BO3\),也就是 3 局 2 胜制。主要比赛的每个次要比赛也是 \(BO3\)。现在需要看看从这 7 个位置开始获取每场次要比赛的结果,先求得每场主要比赛的结果,再求整场比赛谁获胜。

从第一个位置开始比赛,\(T=1010101|1010101\dots\),次要比赛的结果为 1:2 2:1 0:2 2:1 1:2... 即 \(10101|10101\dots\),主要比赛的结果为 1:2 1:2...,即 \(00\dots\),也就是 A 队将以 2:1获胜,结果为 \(1\)。

从第二个位置开始比赛,\(T=0101011|0101011\dots\),次要比赛的结果为 \(01101|01101\dots\),主要比赛结果为 \(101\dots\) 也就是 A 队将以 2:1 获胜,结果为 \(1\).

继续从第三个位置开始比赛……直到从最后一个位置开始比赛。得到最终结果为 \(1110111\)。

Solution

将问题简化为这样的形式:

将循环串 \(T\) 以如下规则生成新循环串 \(T^\prime\):

将串 \(T\) 展开。按照顺序在 \(T\) 上进行游戏。如果数字 \(0\)(或数字 \(1\))的累计数量等于胜利条件 \(a\) ,则该小局的胜利方为 \(0\)(或 \(1\)),立即结束该小局游戏并开始新局。在这样生成的“胜利0-1串”上重复上述的游戏规则,当某个数字的累计数量等于胜利条件 \(b\),则该大局结束,得到大局的胜利方。

稍微写点模拟代码演示:

for(int x=1;x<=n;x++)
{
int y=x;
int cnt[2]={0,0};
cnt[T[x]]++;
while(1)
{
if(cnt[0]>=a||cnt[1]>=a)// or >=b
{
if(cnt[0]>=b) cout<<0;
else cout<<1;
break;
}
y++;
if(y>n) y-=n;
cnt[T[y]]++;
}
}

这是一个嵌套问题。我们先考虑内层问题。

注意到上述代码中的 y 只会向右循环移动,可以用循环双指针在 \(O(n)\) 时间复杂度内解决内层问题。

我们可以得到:在 \(x\) 处开始进行一个小局,游戏结束时获胜方为谁、结束之后下一个小局从哪里开始进行。

但是从小局的胜利情况推大局的胜利情况就不能用双指针如法炮制了。因为此时面对的不是一个循环串 \(T\),而是一个内向基环树森林,我们需要将每个点作为起点跑双指针,这样会超时的。

考虑倍增。

我们定义 \(jump_{x,k}\) 为从 \(x\) 开始,进行 \(2^k\) 个小局之后,接下来会从何处继续游戏。定义 \(cnt_{x,k,0/1}\) 为从 \(x\) 开始,进行了 \(2^k\) 个小局中,A/B 队获胜的数量。

在上面,我们已经用双指针求出了每一个起点 \(x\) 开始一小局的胜利方(令 \(cnt_{x,0,胜利方}=1\))和下一局的起点 \(jump_{x,0}\)。由此预处理 \(k=1,2,3\dots\) 的情况。

现在对于每个起点 \(x\),我们进行倍增。类似于倍增求 LCA,我们倍增地找到最远的大局结束点 \(y\),使得这之间双方胜利数量都刚好小于 \(b\)。此时,再向后走一步(\(y=jump_{y,0}\))一定会导致这一大局结束,所以接下来的这一小局的获胜方就是从 \(x\) 开始玩一大局的获胜方了。

Code

#define N 100010

int n,a,b;
string s;
int jump[N][31];
LL cnt[N][31][2];
string ans; int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t=1;
// cin>>t;
while(t--)
{
cin>>n>>a>>b;
cin>>s;
int l=0,r=0;
int cnt0[2]={0,0};
cnt0[s[0]-'0']=1;
while(1)
{
if(l>=s.size()) break;
if(cnt0[0]>=a||cnt0[1]>=a)
{
bool win=(cnt0[1]>=a);
cnt[l][0][win]=1;
jump[l][0]=r+1;
cnt0[s[l]-'0']--;
l++;
continue;
}
r++;
if(r>=s.size()) r-=s.size();
cnt0[s[r]-'0']++;
} // for(int i=0;i<s.size();i++) cout<<cnt[i][0][0]<<" "<<cnt[i][0][1]<<endl; for(int k=1;k<=30;k++)
{
for(int i=0;i<s.size();i++)
{
jump[i][k]=jump[jump[i][k-1]][k-1];
cnt[i][k][0]=cnt[i][k-1][0]+cnt[jump[i][k-1]][k-1][0];
cnt[i][k][1]=cnt[i][k-1][1]+cnt[jump[i][k-1]][k-1][1];
}
} for(int i=0;i<s.size();i++)
{
int x=i;
int now[2]={0,0};
for(int k=30;k>=0;k--)
{
if(now[0]+cnt[x][k][0]<b&&now[1]+cnt[x][k][1]<b)
{
now[0]+=cnt[x][k][0];
now[1]+=cnt[x][k][1];
x=jump[x][k];
}
}
ans.push_back(cnt[x][0][1]+'0');
// if(now[0]==b) ans.push_back('0');
// if(now[1]==b) ans.push_back('1');
} cout<<ans<<endl; }
return 0;
}

2024牛客多校3J Rigged Games的更多相关文章

  1. 2019牛客多校第一场 I Points Division(动态规划+线段树)

    2019牛客多校第一场 I Points Division(动态规划+线段树) 传送门:https://ac.nowcoder.com/acm/contest/881/I 题意: 给你n个点,每个点有 ...

  2. 牛客多校第一场 B Inergratiion

    牛客多校第一场 B Inergratiion 传送门:https://ac.nowcoder.com/acm/contest/881/B 题意: 给你一个 [求值为多少 题解: 根据线代的知识 我们可 ...

  3. 2019牛客多校第二场 A Eddy Walker(概率推公式)

    2019牛客多校第二场 A Eddy Walker(概率推公式) 传送门:https://ac.nowcoder.com/acm/contest/882/A 题意: 给你一个长度为n的环,标号从0~n ...

  4. 牛客多校第三场 F Planting Trees

    牛客多校第三场 F Planting Trees 题意: 求矩阵内最大值减最小值大于k的最大子矩阵的面积 题解: 矩阵压缩的技巧 因为对于我们有用的信息只有这个矩阵内的最大值和最小值 所以我们可以将一 ...

  5. 牛客多校第三场 G Removing Stones(分治+线段树)

    牛客多校第三场 G Removing Stones(分治+线段树) 题意: 给你n个数,问你有多少个长度不小于2的连续子序列,使得其中最大元素不大于所有元素和的一半 题解: 分治+线段树 线段树维护最 ...

  6. 牛客多校第四场sequence C (线段树+单调栈)

    牛客多校第四场sequence C (线段树+单调栈) 传送门:https://ac.nowcoder.com/acm/contest/884/C 题意: 求一个$\max {1 \leq l \le ...

  7. 牛客多校第3场 J 思维+树状数组+二分

    牛客多校第3场 J 思维+树状数组+二分 传送门:https://ac.nowcoder.com/acm/contest/883/J 题意: 给你q个询问,和一个队列容量f 询问有两种操作: 0.访问 ...

  8. 2019牛客多校第八场 F题 Flowers 计算几何+线段树

    2019牛客多校第八场 F题 Flowers 先枚举出三角形内部的点D. 下面所说的旋转没有指明逆时针还是顺时针则是指逆时针旋转. 固定内部点的答案的获取 anti(A)anti(A)anti(A)或 ...

  9. 2019年牛客多校第一场B题Integration 数学

    2019年牛客多校第一场B题 Integration 题意 给出一个公式,求值 思路 明显的化简公式题,公式是分母连乘形式,这个时候要想到拆分,那如何拆分母呢,自然是裂项,此时有很多项裂项,我们不妨从 ...

  10. 2020牛客多校第八场K题

    __int128(例题:2020牛客多校第八场K题) 题意: 有n道菜,第i道菜的利润为\(a_i\),且有\(b_i\)盘.你要按照下列要求给顾客上菜. 1.每位顾客至少有一道菜 2.给顾客上菜时, ...

随机推荐

  1. 【MATLAB习题】铰链四杆机构的运动学分析

    铰链四杆机构题目&已知数据 matlab 代码 主程序文件: function main %输入已知数据 clear; i1=101.6; i2=254; i3=177.8; i4=304.8 ...

  2. 遍历列表、元组或字符串的函数enumerate

    这两天在处理遇到的问题,循环遍历列表中的字典并输出到excel中 查阅资料发现了一个正和我意的函数 所以周一一上班我就开始试一试 然而发现 enumerate函数只适用于列表.元组或字符串的函数 语法 ...

  3. .NET Core 中如何实现缓存的预热?

    在构建高性能的 .NET Core 应用时,缓存是提升系统响应速度.减轻数据库压力的利器.然而,缓存并非一蹴而就,它也需要"热身"才能发挥最佳性能.这就是缓存预热的意义所在. 一. ...

  4. python ImportError: libGL.so.1: cannot open shared object file: No such file or directory

    前言 python 报错python ImportError: libGL.so.1: cannot open shared object file: No such file or director ...

  5. php去除金额后面多余的0(零)

    第一种: 使用floatval() 第二种: rtrim(rtrim($str, '0'), '.'); 比如$str=2.360000; 最后会输出2.36 第三种使用正则: /** * 去除多余的 ...

  6. 编写你的第一个 Django 应用程序,第4部分

    本教程从教程 3 停止的地方开始.我们是 继续民意调查应用程序,并将专注于表单处理和 减少我们的代码. 一.编写最小表单 让我们更新上一个教程的投票详细信息模板("polls/detail. ...

  7. 抽象类的注意事项、abstract关键字的冲突--java进阶day02

    1.注意事项 1.抽象类不允许创建对象 2.抽象类存在构造方法 3.抽象类中可以存在普通成员方法 4.抽象类的子类存在两种处理方式 第一种不多解释,主要讲第二种,子类继承了抽象类,相当于子类里面有了抽 ...

  8. 修显示器led屏幕能亮但是显示异常

    用电吹风热风大风   对着显示器的  ' led 区域 '  吹十分钟 吹显示器线插口 电源线 插口 机箱    断电吹  // 温度挺高  还得吹显卡接口 线也要换新的 插口需要用线的接口 打磨金属 ...

  9. Python科学计算系列5—导数

    1.一元函数的导数 例1:求下列函数的导数 例2:求下列函数的50阶导数 代码如下: from sympy import * x = symbols('x') f1 = diff(tan(x)) f2 ...

  10. mybatis的mapper接口动态代理开发

    一.必须遵守的四项原则 1:接口 方法名==xx.xml中的id名 2:方法返回值类型与Mapper.xml文件中返回值类型一致 3:方法的入参类型与Mapper.xml文件中入参值类型一致 4:命名 ...