牛客小白月赛53F

传送门

ieda1

以dp[i]表示以第i个人为结尾的方案数,那么答案就是每个人结尾的总和,时间复杂度为O(n^2)

#include <bits/stdc++.h>
using namespace std;
#define accelerate ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl "\n";
#define mod 998244353
#define ll long long
#define PII pair<int,int>
#define INF 0x3f3f3f3f
const int N=2e5+10;
ll n,m,k,x,y,T;
string s[N];
ll dp[N];
bool f(string s1,string s2){
	for(int i=0;i<m;i++){
		if(s1[i]=='o'&&s2[i]=='o') return 0;
	}
	return 1;
}
int main(){
	accelerate;
	cin>>n>>m;
	s[0]="11111111111111111";
	for(int i=1;i<=n;i++) cin>>s[i];
	dp[0]=1;
	ll ans=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=i-1;j++){
			if(f(s[i],s[j])) dp[i]+=dp[j];
	}
	dp[i]%=mod;
	ans=(ans+dp[i])%mod;
	}
	cout<<ans;
	return 0;
}

ieda2

注意m的最大值为16,因此可以想到枚举状态,那么以dp[i]表示表示队列最后一个人的状态为i时的总方案,时间复杂度O(n*2^m)

#include <bits/stdc++.h>
using namespace std;
#define accelerate ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl "\n";
#define mod 998244353
#define ll long long
#define PII pair<int,int>
#define INF 0x3f3f3f3f
const int N=2e5+10;
ll n,m,k,x,y,T;
string s;
ll dp[N];
ll ans=0;
int main(){
	accelerate;
	cin>>n>>m;
	ll Maxx=1<<16;
	while(n--){
		cin>>s;
		int a=0;
		for(int j=0;j<m;j++) a=(a<<1)|(s[j]=='o');
		ll tmp=1;
		tmp+=dp[a];
		(ans+=tmp)%=mod;
//		cout<<ans<<endl;
		for(int j=0;j<Maxx;j++){
			if(!(a&j)) (dp[j]+=tmp)%=mod;
		}
	}
	cout<<ans;
	return 0;
}

Idea3

定义f[i][j]为以一个以i为高8位,j为低8位的这样的状态为队列末尾的可行总方案,那么当我们得到一个数时,就可以枚举合格的高8位,然后加上对应的低8位的可行方案,就可以得到以该状态为末尾的方案总数,然后再枚举低8位,给对应的高8八位的dp数组加上该数的总方案,既可以更新数组,相较于Idea2,这种方案通过对每个数进行高8位的分类,使得状态之间的传递更加快捷,时间复杂度为O(2*n*2^(m/2))

#include<bits/stdc++.h>
using namespace std;

const int mod = 998244353;
int f[1 << 8][1 << 8];
//f[i][j]以i为高8位中,总以j为低8位可以放下的数
//f[i][j]表示当前队列最右边的人的状态高8位为hi
//且低8位和li没有交集的总方案数,也就是可行的
char s[17];

int main() {
	int n, m; cin >> n >> m;
	int ans = 0;
	while (n--) {
		cin >> s;
		int a = 0;
		for (int i = 0; i < m; ++i) {
			a = (a << 1) | (s[i] == 'o');//二进制转换
		}
		int x = a >> 8,y = a & 255, z = 1;//高8位,低8位,
		for (int i = 0; i < (1 << 8); ++i) {
			if (!(x & i)) {//枚举合格的高8位中那些可以后面接上
				(z += f[i][y]) %= mod;
			}
		}
		(ans += z) %= mod;
		for (int i = 0; i < (1 << 8); ++i) {
			if (!(y & i)) {//枚举以ai的高8位的基础上那些低8位可以放下ai
				(f[x][i] += z) %= mod;
			}
		}
	}
	cout << ans << endl;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值