“蔚来杯“2022牛客暑期多校训练营4-D-Jobs (Easy Version)

"蔚来杯"2022牛客暑期多校训练营4-D-Jobs (Easy Version)

原题链接:https://ac.nowcoder.com/acm/contest/33189/D

题目大意

n n n个公司,每个公司有 m i m_i mi个工作,每个工作对一个人的"三商"——IQ,EQ,AQ有最低限度。现在有 q q q个人来应聘,他们的"三商"由给定种子(seed)所产生的随机数决定。当一个人的"三商"全部大于等于一个工作所对应的"三商"后,他就会收到该公司的offer(即使他满足一个公司的多个工作需求,也只会受到 1 1 1个offer)。现在求答案:( p i p_i pi为第i个人能收到的offer数)
( ∑ i = 1 q p i . s e e d q − i ) m o d 998244353 (\sum_{i=1}^{q}p_i.seed^{q-i}) mod 998244353 (i=1qpi.seedqi)mod998244353

解析

由于"三商"的范围在 1 − 400 1-400 1400之间,我们可以用一个三维数组 m a p p i , j , k mapp_{i,j,k} mappi,j,k来表示当一个人 I Q , E Q , A Q IQ,EQ,AQ IQ,EQ,AQ分别为 i , j , k i,j,k i,j,k时能收到offer的状态。由于一个公司可能有多个工作,差分不能直接统计offer的数量,但 n n n最大为 10 10 10,因此我们可以用二进制数来保存这个状态,若第 i − 1 i - 1 i1位为1,就说明他会收到 i i i公司的offer。然后从三个不同维度分别差分,后一位要 ∣ = |= =(位或)前一位,这样同一个公司的offer数就不会被多次计算,offer总数也能统计。这样对于每一对 I Q , E Q , A Q IQ,EQ,AQ IQ,EQ,AQ,只需要 O ( 1 ) O(1) O(1)的时间复杂度,预处理也只需 O ( n 3 ) O(n^3) O(n3)的复杂度,还是可以通过的。 s e e d q − i seed^{q-i} seedqi使用快速幂计算即可。

代码展现

#include<iostream>
#include<cstdio>
#include<random>

using namespace std;

typedef long long ll;

const ll mod = 998244353;

int n,q,seed,mapp[410][410][410];

ll ksm(ll x,ll p){
	ll res = 1;
	while(p){
		if(p % 2){
			res = res * x % mod;
		}
		x = x * x % mod;
		p /= 2;
	}
	return res;
}

int solve(int IQ,int EQ,int AQ){
	return mapp[IQ][EQ][AQ];
}

int main(){
	ll ans = 0;
	scanf("%d%d",&n,&q);
	
	for(int i=1;i<=n;i++){
		int t;
		scanf("%d",&t);
		for(int j=1;j<=t;j++){
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			mapp[a][b][c] |= (1 << (i - 1));
			// 将第i - 1位设为1
		}
	}
	
	// 三维差分
	for(int i=1;i<=405;i++){
		for(int j=1;j<=405;j++){
			for(int k=2;k<=405;k++){
				mapp[i][j][k] |= mapp[i][j][k - 1];
			}
		}
	}
	
	for(int i=1;i<=405;i++){
		for(int j=1;j<=405;j++){
			for(int k=2;k<=405;k++){
				mapp[i][k][j] |= mapp[i][k - 1][j];
			}
		}
	}
	
	for(int i=1;i<=405;i++){
		for(int j=1;j<=405;j++){
			for(int k=2;k<=405;k++){
				mapp[k][j][i] |= mapp[k - 1][j][i];
			}
		}
	}
	
	for(int i=1;i<=405;i++){
		for(int j=1;j<=405;j++){
			for(int k=1;k<=405;k++){
				int num = 0;
				while(mapp[i][j][k]){
					if(mapp[i][j][k] % 2){
						num++;
					}
					mapp[i][j][k] /= 2;
				}
				mapp[i][j][k] = num;
			}
		}
	}
	
	scanf("%d",&seed);
	std::mt19937 rng(seed);
	std::uniform_int_distribution<> u(1,400);
	int lastans=0;
	for (int i=1;i<=q;i++)
	{
	    int IQ=(u(rng)^lastans)%400+1;  // The IQ of the i-th friend
	    int EQ=(u(rng)^lastans)%400+1;  // The EQ of the i-th friend
	    int AQ=(u(rng)^lastans)%400+1;  // The AQ of the i-th friend
	    lastans=solve(IQ,EQ,AQ);  // The answer to the i-th friend
	    ans = (ans + (ll)lastans * ksm(seed,q - i) % mod ) % mod;
	}
	printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值