2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1)(部分题解)

比赛链接

C.Contest Setting

题意:有n个题目,每个题目有相应的难度等级。选择其中k个题目出一场比赛,要求一场比赛中不能出现相同难度的题目。问一共可以组多少场比赛。

题解:一开始会自然而然的想到排列组合。但是下面这组数据,1 1 2 2 3 3 4 4 4,让你从中选两个不能重复的显然不好去排列组合求方案数。于是神奇的dp来了。

  • 首先题目难度的范围比较大,可以先离散化再计数,也可以直接用map计数(我用的是map)。
  • 设dp[i][j]为前i个题目里选择了j个题目的方案数。
  • 状态转移方程就是dp[i][j] = dp[i-1][j] + dp[i-1][j-1]*mp[a[i]];
  • dp[i][j] += dp[i-1][j]   第i个题目不选,那么现在需要j个题目,所以就等于前i-1个题目选择了j个题目的方案数。
  • dp[i][j] += dp[i-1][j-1]*mp[a[i]]  选择了第i个题目,那么现在还需要j-1个题目,所以就等于前i-1个题目选择了j-1个题目的方案数乘以当前题目的个数。
  • 最后输出前n个题目选择了k个题目的方案数就是答案了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll Mod = 998244353;
ll dp[1010][1010];
int a[1010];
int main()
{
 	int n,k;
 	cin>>n>>k;
 	map<int,int>m;
 	for(int i=1;i<=n;i++){
 		cin>>a[i];
 		m[a[i]]++;//map计数 
	}
	
	sort(a+1,a+1+n);
	n = unique(a+1,a+1+n) - a - 1;//去重
	 
	for(int i=0;i<=n;i++) 
	      dp[i][0]=1;//dp初始化 
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=k;j++){
			dp[i][j] = (dp[i-1][j-1]*m[a[i]]+dp[i-1][j])%Mod;
		}
	}
	
	cout<<dp[n][k]<<endl;
}  

D.Count The Bits

题意:给你一个k和b,b代表的是二进制的位数,k代表的是除数。求[0,2^b-1]是k倍数的数,二进制下1的个数。

题解

先了解一个前置知识,如何求前2^b个数二进制下1的个数。

  • 设dp[i]为前 2^i 个数中二进制下1的个数。
  • 状态转移方程就为 dp[i] = dp[i-1] + 2^(i-1) + dp[i-1];为啥dp[i-1]要分开写呢?
  • 假设此时i为3,那么前2^i数中1的个数就相当于在前2^(i-1)二进制下第i位分别补0和补1的情况。

  • 第i位补0就是dp[i-1]。
  • 第i位补1就是dp[i-1]加上补了1的个数就是2^(i-1)。

然后再回到这个题

  • 设f[i][j]表示前2^i个数余k等于j的个数;
  • f[i][j] = f[i-1][j] + f[i-1][ j - 2^(i-1)%k ];类似于上面那个dp方程
  • 最高位如果补0,那么就是f[i-1][j]。
  • 最高位如果补1,就相当于这个数加上了2^(i-1) ,那么他的余数就加上了2^(i-1)%k 。
  • 谁加上2^(i-1)%k 等于 j,当然只有j -2^(i-1)%k 
  • 设d[i][j]]表示前2^i个数余k等于j的数二进制下1的个数;
  • d[i][j] = d[i-1][j] + d[i-1][j - 2^(i-1)%k] + f[i-1][ j - 2^(i-1)%k ] 
  • 最高位补0,d[i][j] += d[i-1][j]
  • 最高位补1 ,也是余数多了2^(i-1)%k, 那么d[i][j] += d[i][ j - 2^(i-1)%k] , 多加的 f[i-1][ j - 2^(i-1)%k ] 实际上就是最高位补上的1的个数

因为(j-2^(i-1)%k) 可能是负数,所以要写成 (j- 2^(i-1)%k + k)%k

然后可以明显看出当前状态只和前面一个状态有关系,所以为了节省空间,可以使用滚动数组,具体看代码。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1100;
const ll Mod = 1e9 + 9;
ll d[2][maxn],f[2][maxn];
ll powmod(ll a,ll b,ll mod) {ll res=1;a%=mod; 
assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
int main()
{
	ll k,b;
	cin>>k>>b;
	f[0][0]=1;
	for(int i=1;i<=b;i++){
		for(int j=0;j<=k-1;j++){
			d[i%2][j] = (d[(i+1)%2][j] + d[(i+1)%2][(j-powmod(2,i-1,k)%k+k)%k] + f[(i+1)%2][(j-powmod(2,i-1,k)%k+k)%k])%Mod;
			f[i%2][j] = (f[(i+1)%2][j] + f[(i+1)%2][(j-powmod(2,i-1,k)%k+k)%k])%Mod;
		}
	}
	cout<<d[b%2][0]%Mod<<endl;
}

  •  

猜你喜欢

转载自blog.csdn.net/qq_42129242/article/details/94714944