此后如竟没有炬火 我便是唯一的光|

Day_Dreamer_D

园龄:3年6个月粉丝:3关注:16

2022-10-01 18:49阅读: 280评论: 0推荐: 0

洛谷 P2567 [SCOI2010]幸运数字 题解

主要思路

显然我们需要求出所有范围内的合法幸运号码,然后容斥。

容斥就是暴搜+剪枝。

剪枝&优化

  1. 一个幸运号码是另一个幸运号码的倍数,则这个数不会产生任何贡献,剪枝。
  2. 选定数的 LCM 大于 r 则直接退出。
  3. 将幸运号码从大到小排序,这样搜索的时候能更快突破上界。
  4. 对于所有大于 r3 的幸运号码,不能与其他任何数字合并,于是我们直接预先算出这些数字,对剩余部分暴搜。

代码

//P2567 [SCOI2010]幸运数字
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MOD=1e9+7;
int cnt;
ll a[5050],res,l,r;
bool vis[5050];
ll math_GCD(ll x,ll y)
{
return !y?x:math_GCD(y,x%y);
}
void _DFS(ll x)//不开 long long 就会 RE
{
if(x>r)
{
return;
}
if(x)
{
a[++cnt]=x;
}
_DFS(x*10+6);
_DFS(x*10+8);
return;
}
bool _check(ll a,ll b)//大于则直接剪枝
{
ll x=a/r,y=b/r;
if(x*y)
{
return true;
}
return x*y>r;
}
void _calc(int x,ll sum,int count)
{
ll d;//不开 long long 就会 RE
if(sum>r)//超过上界则直接返回
{
return;
}
if(x>cnt)
{
if(sum!=1)
{
if(count&1)
{
res+=r/sum-l/sum;
}
else
{
res-=r/sum-l/sum;
}
}
return;
}
_calc(x+1,sum,count);
d=a[x]/math_GCD(sum,a[x]);
if(!_check(sum,d))
{
_calc(x+1,sum*d,count+1);
}
return;
}
signed main()
{
int temp=0;
scanf("%lld%lld",&l,&r);
l--;
_DFS(0);
sort(&a[1],&a[cnt+1]);
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<i;j++)
{
if(a[i]%a[j]==0)//若为倍数,没有贡献,剪枝
{
vis[i]=true;
break;
}
}
}
for(int i=1;i<=cnt;i++)
{
if(!vis[i])
{
if(a[i]<=r/3)
{
a[++temp]=a[i];
}
else
{
res+=r/a[i]-l/a[i];
}
}
}
cnt=temp;
reverse(&a[1],&a[cnt+1]);
_calc(1,1,0);
printf("%lld\n",res);
return 0;
}
/*
* 洛谷
* https://www.luogu.com.cn/problem/P2567
* C++20 -O0
* 2020.10.1
*/

本文作者:Day_Dreamer_D

本文链接:https://www.cnblogs.com/2020gyk080/p/16747595.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Day_Dreamer_D  阅读(280)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起