D. AquaMoon and Chess(组合数学)

Problem - D - Codeforces

 Cirno给了AquaMoon一个大小为1 × n的棋盘,它的单元格从左到右用从1到n的整数编号。在开始时,一些单元格最多被一个兵占据,而其他单元格则是空的。在每个操作中,AquaMoon可以选择一个带有卒的单元格i,并执行以下任一操作(如果可能的话):移动卒到(i+ 2)-th单元格,如果i+2 < n并且(i+1)-th单元格被占用,(i+ 2)-th单元格未被占用。如果i-22 1和(i-1)-单元格被占用,(i-2)-单元格未被占用,则将卒从它移动到(-2)-第(-2)单元格。这是棋盘的初始状态。AquaMoon希望通过一些操作序列来计算从初始状态可到达的状态数。但她不擅长编程。你能帮帮她吗?由于答案可能很大,请对998 244 353取模。输入输入由多个测试用例组成。第一行包含一个整数t (1 < t 10 000)测试用例的数量。第一行包含一个整数n (1 <n < 105)——棋盘的大小。第二行包含n个字符,由字符“O”和“1”组成。如果第i个字符为“1”,则第i个单元格被初始化;否则,第i个单元格最初是空的。它保证所有测试用例的n和不超过105。输出对于每个测试用例,用模998 244 353的操作序列输出从初始状态可达的状态数。

Example

input

Copy

6
4
0110
6
011011
5
01010
20
10001111110110111000
20
00110110100110111101
20
11101111011000100010

output

Copy

3
6
1
1287
1287
715

题解:
通过观察,单独一个1的情况不能移动

连续两个1可以随便移动,

连续三个1,会遗漏一个1剩下随便移动

也就是偶数连续1随便移动,否则遗漏一个1

举个例子:
11111100010010
将位置5,6的1往后移到第一个单独的1的位置时,它是长这样的:
11110001110010,然后往后移一格:
11110001011010
那么可以发现,前面三个0依旧在,后面的相对位置也没变,也就是说两个1的移动不会对这个字符串的其它元素的相对位置造成影响。并且可以发现,单独的1是否存在对于字符串也不会有任何影响。那么上面的字符串可以看成:
111111000000
到现在我们可以知道答案只和连续的偶数1和零的个数会有影响。
那么我们需要将两个相邻1看成一个整体进行移动,于是上面这个字符串也就可以看成:
三个1和六个0有多少种组合方式。答案也就呼之欲出了:假设有n个11,m个0,他们的组合情况数

原文链接:Codeforces 1546 D. AquaMoon and Chess —— 组合数学,一点点想法_codeforces - 1546d_天翼之城*的博客-CSDN博客

 

#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
ll mod = 998244353;
ll fac[100050];
ll infac[100050];
ll ksm(ll x,ll p)
{
	ll ans = 1;
	while(p)
	{
		if(p&1)
		ans = ans*x%mod;
		x = x*x%mod;
		p /= 2;
	}
	return ans;
}
char s[100050];
void solve()
{
	int n;
	scanf("%d",&n);
	scanf("%s",s + 1);
	int one = 0,zero = 0;
	int sum = 0;
	for(int i = 1;i <= n;i++)
	{
		if(s[i] == '0')
		{
			zero++;
			one += sum/2;
			sum = 0;
		}
		else
		{
			sum ++;
		}
	}
	one += sum/2; 
		ll w = fac[one + zero]*infac[one]%mod*infac[zero]%mod;
		printf("%lld\n",w);
}
signed main() 
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
	int t = 1;
	fac[0] = 1;
	for(int i = 1;i <= 100000;i++)
	{
		fac[i] = fac[i - 1]*i%mod;
	}
	infac[100000] = ksm(fac[100000],mod - 2);
	for(int i = 99999;i >= 0;i--)
	{
		infac[i] = infac[i+1]*(i+1)%mod;
	}
    scanf("%d",&t);
	while (t--) 
	{
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值