BZOJ 1853 幸运数字(容斥原理+dfs)
题意:求闭区间内能被6和8组成的数字整除的数目。n<=1e11.
我们可以预处理出这些6和8组成的数字,大概2500个,然后排除一些如88,66的情况。这样大概还剩下1000个。
转化为[0,r]和[0,l-1]的问题,显然需要运用容斥原理。ans=n/6+n/8+n/68+...+...-n/lcm(6,8)-n/lcm(6,68)......
因此用dfs即可计算出来,这样一看复杂度好像是2^1000的样子,但是注意到lcm增长的很快,如果lcm>n那么显然之后的这些情况就可以忽略了。
这就是一个强有力的剪枝。
另外从大到小dfs要比从小到大dfs要好。大概常数小?
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-9 # define MOD 1024523 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=500005; //Code begin... LL num[2505], pos1, pos2, p[15], mark[1005]; void init(){ p[0]=1; FOR(i,1,10) p[i]=p[i-1]*10; int l=1, r=2, tmpl, tmpr; num[++pos1]=6; num[++pos1]=8; FOR(i,2,10) { tmpl=r+1; FOR(j,l,r) num[++pos1]=6*p[i-1]+num[j]; FOR(j,l,r) num[++pos1]=8*p[i-1]+num[j]; tmpr=pos1; l=tmpl; r=tmpr; } FOR(i,1,pos1) { int flag=true; FO(j,1,i) if (num[i]%num[j]==0) {flag=false; break;} if (flag) mark[++pos2]=num[i]; } mark[++pos2]=1e16; } LL dfs(int pos, int flag, LL x, LL cheng){ if (pos<=0) return 0; LL res=0; res+=dfs(pos-1,flag,x,cheng); LL tmp=__gcd(cheng,mark[pos]); if (cheng/tmp<=(double)x/mark[pos]) { LL tt=cheng/tmp*mark[pos]; res+=dfs(pos-1,flag^1,x,tt); res+=(flag?x/tt:-x/tt); } return res; } LL sol(LL x){ for (int i=pos2; i>=1; --i) if (mark[i]<=x) return dfs(i,1,x,1); return 0; } int main () { init(); LL a, b; scanf("%lld%lld",&a,&b); printf("%lld\n",sol(b)-sol(a-1)); return 0; }