2021NOIP提高组

2021NOIP提高组

T1报数(普及/提高-)

桶排序/筛质数

看到题目我首先的想法是像欧拉筛一样以 O ( n ) O(n) O(n)的时间复杂度将7的倍数以及含有7的数字以及其倍数找出来,并进行标记,接下来将未被标记的数存储在vector数组里,然后可以用二分查找 O ( l o g n ) O(logn) O(logn)的时间复杂度查找vector数组中是否有这个数,如果有则输出它的下一位,否则输出-1。

#include<bits/stdc++.h>
using namespace std;
const int N=1e7+100;
bool s[N];
int ans[N];
int main(){
	for(int i=1;i<=1e7+10;i++){
		if(s[i]) continue;
		int temp=i;
		while(temp){
			int tmp=temp%10;
			if(tmp==7){
				for(int j=1;j*i<=N;j++) s[j*i]=1;
				break;
			}
			temp=temp/10;
			
		}
		
	}
	int cnt=0;
	for(int i=1;i<=N;i++){
		if(!s[i]) ans[++cnt]=i;
	}
	int t;
	cin>>t;
	while(t--){
		int x;
		cin>>x;
		int idx=lower_bound(ans+1,ans+cnt+1,x)-ans;
		if(s[x]) cout<<"-1"<<endl;
		else cout<<ans[idx+1]<<endl;
	}
}
T2方差(提高+/省选-)

记忆化搜索(50)
计数dp(100)

将题目简化一下,题目要求我们从 0 − m 0-m 0m m + 1 m+1 m+1个数当中选择 n n n个排成一个序列 a a a,并要求这个序列所组成的 S = 2 a 1 + 2 a 2 + ⋯ + 2 a n S=2^{a_1}+2^{a_2}+\dots+2^{a_n} S=2a1+2a2++2an中一的个数不大于 K K K个。

通过分析我们发现序列 a a a中的数其实就是代表 S S S在位置 a i a_i ai中有一个1,但是因为 a a a中的数可以重复,这就牵扯到了进位的问题。例如:假设 a a a中序列有两个2,那么 S S S的二进制在第2位上就会是0而不是1,因为二进制进位。

进一步分析,因为二进制进位只会从低位到高位,我们从第0位开始考虑:假设 a a a序列中只有一个0,那么S的二进制第 0 0 0位就是1,接下来我们只需考虑 1 − m 1-m 1m位中,选择n-1个,有 K − 1 K-1 K1个1的情况,以及0在 a a a这个序列中的位置即可,这就成功的缩小了范围。

但是如果 a a a序列中有多个 0 0 0呢?这就涉及到了进位问题,假设有 k k k个0,那么 S S S中第 0 0 0位就是 k   m o d   2 k\ mod\ 2 k mod 2,然后向前进 ⌊ k / 2 ⌋ ⌊k/2⌋ k/2位,那么我们就要考虑在 1 − m 1-m 1m位中选择 n − k n-k nk个,有 K − k   m o d   2 K-k\ mod\ 2 Kk mod 2个1的情况,以及 0 0 0 a a a这个序列中的位置。

化为更一般的情况,设 d p [ i ] [ j ] [ k ] [ p ] dp[i][j][k][p] dp[i][j][k][p]表示目前已经填了 i i i位,序列 a a a中的元素已经用了 j j j个,现在序列构成的 S S S中有 k k k个1,并将向下一位进 p p p位。因为低位的进位会对高位有影响,而高位的进位对低位没有影响,所以我们这道题的转移方向可以从低位到高位转移,即:
d p [ i ] [ j ] [ k ] [ p ] − > d p [ i + 1 ] [ j + t ] [ k + t + p & 1 ] [ t + p > > 1 ] dp[i][j][k][p]->dp[i+1][j+t][k+t+p\&1][t+p>>1] dp[i][j][k][p]>dp[i+1][j+t][k+t+p&1][t+p>>1]
其中 t t t指的是在 a a a序列中有 t t t i i i,因为序列是有序的,所以我们也要考虑 i i i a a a序列中的位置。那么就有下面的式子:
d p [ i + 1 ] [ j + t ] [ k + t + p & 1 ] [ t + p > > 1 ] + = C n − j t ∗ v [ i ] [ t ] ∗ d p [ i ] [ j ] [ k ] [ p ] dp[i+1][j+t][k+t+p\&1][t+p>>1]+=C_{n-j}^{t}*v[i][t]*dp[i][j][k][p] dp[i+1][j+t][k+t+p&1][t+p>>1]+=Cnjtv[i][t]dp[i][j][k][p]
值得注意的是,最高位也有可能会进位,所以我们最后在统计答案时,要统计上第 m + 1 m+1 m+1位上有几个1,如果加上现有的1的个数不超过 K K K

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[105][50][50][50];
int v[105];
ll c[50][50];
ll pv[105][35];
const int mod=998244353;
void init(){
	for(int i=0;i<=35;i++){
		c[i][0]=1;
		for(int j=1;j<=i;j++){
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
		}
	}
}
ll com(int x){
	int res=0;
	while(x){
		res+=x&1;
		x>>=1;
	}
	return res;
}
int main(){
	int n,m,K;
	cin>>n>>m>>K;
	init();
	//cout<<c[5][3]<<endl;
	for(int i=0;i<=m;i++){
		cin>>v[i];
		pv[i][0]=1;
		for(int j=1;j<=n;j++){
			pv[i][j]=pv[i][j-1]*v[i]%mod;
		}
	} 
	f[0][0][0][0]=1;
	for(int i=0;i<=m;i++){
		for(int j=0;j<=n;j++){
			for(int k=0;k<=K;k++){
				for(int p=0;p<=(n>>1);p++){
					for(int t=0;t<=n-j;t++){
							f[i+1][j+t][k+(t+p&1)][t+p>>1]=(f[i+1][j+t][k+(t+p&1)][t+p>>1]+f[i][j][k][p]*pv[i][t]%mod*c[n-j][t]%mod)%mod;	//cout<<	f[i+1][j+t][k+(t+p&1)][t+p>>1]<<'a'<<endl;
					}
					
					
				}
			}
		}
	}
	ll ans=0;
	for(int k=0;k<=K;k++){
		for(int p=0;p<=n>>1;p++){
			if(k+com(p)<=K){
				ans=(ans+f[m+1][n][k][p])%mod;
			} 
		} 
	}
	cout<<ans<<endl;
	
}

后两道,咕咕咕

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值