2019暑假牛客多校4.G

题目:
链接:https://ac.nowcoder.com/acm/contest/885/G
来源:牛客网

subsequence 1
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
You are given two strings s and t composed by digits (characters ‘0’ ∼\sim∼ ‘9’). The length of s is n and the length of t is m. The first character of both s and t aren’t ‘0’.

Please calculate the number of valid subsequences of s that are larger than t if viewed as positive integers. A subsequence is valid if and only if its first character is not ‘0’.
Two subsequences are different if they are composed of different locations in the original string. For example, string “1223” has 2 different subsequences “23”.

Because the answer may be huge, please output the answer modulo 998244353.
输入描述:

The first line contains one integer T, indicating that there are T tests.

Each test consists of 3 lines.

The first line of each test contains two integers n and m, denoting the length of strings s and t.

The second line of each test contains the string s.

The third line of each test contains the string t.

  • 1≤m≤n≤30001 \le m \le n \le 30001≤m≤n≤3000.

  • sum of n in all tests ≤3000\le 3000≤3000.

  • the first character of both s and t aren’t ‘0’.

输出描述:

For each test, output one integer in a line representing the answer modulo 998244353.

示例1
输入
复制

3
4 2
1234
13
4 2
1034
13
4 1
1111
2

输出
复制

9
6
11

说明

For the last test, there are 6 subsequences “11”, 4 subsequcnes “111” and 1 subsequence “1111” that are valid, so the answer is 11.

题意:给定一个长度为n的字符串s和一个长度为m的字符串t,s和t都只有字符‘0’到‘9’组成,询问s有几个子序列大于t字符串代表的数字。每个字符的长度为3000。


思路:用容斥定理和lucas算组合数来求解的,对于长度刚好为m的字符串dp求解。dp第一维代表在s字符串中扫到了第i位,dp第二维表示取了j个数字的情况,dp第三位0代表不相等(取的小于t数组),1代表相等(迄今为止都相等)。最后再容斥定理减去取m个数字时比t数组小的的情况数,以及前导0对长度大于m的情况就是答案了。

AC代码:

/**
 *          ┏┓    ┏┓
 *          ┏┛┗━━━━━━━┛┗━━━┓
 *          ┃       ┃  
 *          ┃   ━    ┃
 *          ┃ >   < ┃
 *          ┃       ┃
 *          ┃... ⌒ ...  ┃
 *          ┃              ┃
 *          ┗━┓          ┏━┛
 *          ┃          ┃ Code is far away from bug with the animal protecting          
 *          ┃          ┃   神兽保佑,代码无bug
 *          ┃          ┃           
 *          ┃          ┃        
 *          ┃          ┃
 *          ┃          ┃           
 *          ┃          ┗━━━┓
 *          ┃              ┣┓
 *          ┃              ┏┛
 *          ┗┓┓┏━━━━━━━━┳┓┏┛
 *           ┃┫┫       ┃┫┫
 *           ┗┻┛       ┗┻┛
 */
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=3050;
const int mod=998244353;
ll luc[maxn];
int s[maxn],t[maxn];
int n,m;
ll dp[maxn][maxn][2];//0是不相等,1是相等的情况 

void get_luc(ll mod)
{
	luc[0]=1;
	for(int i=1;i<maxn;i++) luc[i]=(luc[i-1]*i)%mod;
}

ll qk_power(ll a,ll b,ll c)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=(ans*a)%c;
		b=b>>1;
		a=(a*a)%c;
	}
	return ans;
}

ll lucas(ll n,ll m,ll p)
{
	ll ans=1;
	while(n&&m)
	{
		ll a=n%p;ll b=m%p;
		if(a<b) return 0;
		ans=((ans*luc[a]%p)*(qk_power(luc[b]*luc[a-b]%p,p-2,p)))%p;
		n/=p;m/=p;
	}
	return ans;
}

vector<int> tmp;
int main()
{
	get_luc(mod);
	int kase;
	scanf("%d",&kase);
	while(kase--)
	{
		tmp.clear();
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		scanf("%1d",&s[i]);
		for(int i=1;i<=m;i++)
		scanf("%1d",&t[i]);
		for(int i=1;i<=n;i++)
		{
		//	cout<<"---"<<i<<endl;
			if(s[i]==0) tmp.push_back(i);
		}
		for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		dp[i][j][1]=dp[i][j][0]=0;
		for(int i=1;i<=n;i++)
		{
			dp[i][1][0]=dp[i-1][1][0];
			dp[i][1][1]=dp[i-1][1][1];
			if(s[i]<t[1]) dp[i][1][0]++;
			if(s[i]==t[1]) dp[i][1][1]++;
		}
	/*	for(int i=1;i<=n;i++)
		{
			cout<<dp[i][1][0]<<"--"<<dp[i][1][1]<<endl;
		}*/
		
		for(int i=2;i<=n;i++)
		{
			for(int j=2;j<=i;j++)
			{
				dp[i][j][1]=dp[i-1][j][1];
				dp[i][j][0]=dp[i-1][j][0];
				if(s[i]==t[j])
				{
					dp[i][j][1]=(dp[i][j][1]+dp[i-1][j-1][1])%mod;
					dp[i][j][0]=(dp[i][j][0]+dp[i-1][j-1][0])%mod;
				} 
				if(s[i]<t[j]) dp[i][j][0]=(dp[i][j][0]+dp[i-1][j-1][0]+dp[i-1][j-1][1])%mod;
				if(s[i]>t[j]) dp[i][j][0]=(dp[i][j][0]+dp[i-1][j-1][0])%mod;
			}
		 } 
		
		ll ans=0;
		for(int i=1;i<=n;i++)
		{
			ans=(ans+lucas(n,i,mod))%mod;
		}
		for(int i=1;i<=m-1;i++)
		{
			ans=(ans-lucas(n,i,mod)+mod+mod)%mod;
		}
		ans=(ans-dp[n][m][1]-dp[n][m][0]+mod)%mod;
		
		for(int i=0;i<(int)tmp.size();i++)
		{
			int x=tmp[i],t=n-x;
			if(t>=m)
			{
				for(int j=m;j<=t;j++)
				{
					ans=(ans-lucas(t,j,mod)+mod)%mod;
				}
			}
			else break; 
		}
		printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值