专栏文章

题解:B4206 [常州市赛 2021] 数字翻转

B4206题解参与者 12已保存评论 13

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
13 条
当前快照
1 份
快照标识符
@miovmzyd
此快照首次捕获于
2025/12/03 01:53
3 个月前
此快照最后确认于
2025/12/03 01:53
3 个月前
查看原文

  一道非常好的深搜优化题
  暴力算法O(nq)O(nq),由于数据比较水所以加一些优化能够过,上一篇题解关于暴力的讲解已经很清晰了,所以这里不在过多赘述了
  Q:如何优化?
  考虑提前预处理出所有符合要求的数,按照常规暴力思想依旧是枚举每一个数,但是这样太慢了
  那么我们换个思路,将判断可行性转化为构造可行的数
  这里就用到 dfs 来构造啦(具体解释都放在注释里了)
CPP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a1[6]={0,2,5,6,8,9};
ll a2[6]={0,2,5,9,8,6};
//a1储存所有翻转后有意义的数字
//a2储存a1中的数字翻转后的结果 
ll Q,l,r,p[20];
vector<ll> q;//记录所有符合题意的数 
void dfs(ll len,ll ori,ll rev){
	//ori表示当前计算的一段翻转后有意义的数串
	//rev表示ori翻转后的结果 
	//len记录当前ori和rev的长度 
	if(len) q.push_back(ori*p[len]+rev);//两端拼起来符合要求 
	ll st;
	if(len==7) return;//长度达到限制了!(最大不超过10^14 
	if(len==0) st=1;//不能有前导0
	else st=0;
	for(int i=st;i<6;++i){
		if(a1[i]==a2[i]) q.push_back(ori*p[len+1]+a1[i]*p[len]+rev);//可以放在两端中间 
		dfs(len+1,ori*10+a1[i],a2[i]*p[len]+rev);//变换前和变换后的两个数放在两个数串中间也可以 
	}
}
int main(){
	p[0]=1;
	for(int i=1;i<=15;i++) p[i]=p[i-1]*10;//预处理10的次方 
	dfs(0,0,0);//构造所有可行解 
	sort(q.begin(), q.end());//排序 为后面的二分做准备 
	cin>>Q;
	while(Q--){
		cin>>l>>r;
		ll ans=upper_bound(q.begin(),q.end(),r)-lower_bound(q.begin(),q.end(),l);
		//使用二分查找能够很好地降低复杂度 
		cout<<ans<<endl;
	}
	return 0;
}
无注释版本:
CPP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a1[6]={0,2,5,6,8,9};
ll a2[6]={0,2,5,9,8,6};
ll Q,l,r,p[20];
vector<ll> q; 
void dfs(ll len,ll ori,ll rev){
	if(len) q.push_back(ori*p[len]+rev); 
	ll st;
	if(len==7) return; 
	if(len==0) st=1; 
	else st=0;
	for(int i=st;i<6;++i){
		if(a1[i]==a2[i]) q.push_back(ori*p[len+1]+a1[i]*p[len]+rev); 
		dfs(len+1,ori*10+a1[i],a2[i]*p[len]+rev); 
	}
}
int main(){
	p[0]=1;
	for(int i=1;i<=15;i++) p[i]=p[i-1]*10; 
	dfs(0,0,0); 
	sort(q.begin(), q.end()); 
	cin>>Q;
	while(Q--){
		cin>>l>>r;
		ll ans=upper_bound(q.begin(),q.end(),r)-lower_bound(q.begin(),q.end(),l);
		cout<<ans<<endl;
	}
	return 0;
}  

评论

13 条评论,欢迎与作者交流。

正在加载评论...