并不对劲的bzoj1853:[SCOI2010]幸运数字
据说本题的正确读法是【shìng运数字】。
听上去本题很适合暴力,于是并不对劲的人就去写了。其实这题就是一个很普(有)通(趣)暴力+神奇的优化。
首先,会发现幸运数字很少,那么就先搜索出所有幸运数字。
找出每个幸运数字后,会发现每一个数在[a,b]出现了多少次是可以直接算出的,就是floor(b/x)-ceil(a/x)+1。
但是直接加想必是不行的,因为两个数的lcm被算重复了。这时直接容斥会不会T或者出一些奇怪的问题?当然会。所以不能直接容斥,要加些优化再容斥。
1.有些幸运数字是其它幸运数字的倍数,要它何用?
2.当lcm>b时,[a,b]中肯定没有lcm的倍数,要剪掉。
3.lcm(x,y)=x*y/gcd(x,y),会发现x*y可能会爆long long,用double来存。
#include<algorithm> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iomanip> #include<iostream> #include<map> #include<queue> #include<stack> #include<vector> #define rep(i,x,y) for(register LL i=(x);i<=(y);++i) #define dwn(i,x,y) for(register LL i=(x);i>=(y);--i) #define re register #define LL long long #define LCM(x,y) ((x)*(y)/gcd((x),(y))) using namespace std; inline LL read() { LL x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } inline void write(LL x) { LL f=0;char ch[20]; if(!x){puts("0");return;} if(x<0){putchar('-');x=-x;} while(x)ch[++f]=x%10+'0',x/=10; while(f)putchar(ch[f--]); putchar('\n'); } LL ten[15],a,b,luck[8200],ans,inf[3]; int lena,lenb,cnt,tim; int getten(LL x){rep(i,0,10)if(ten[i]<=x&&ten[i+1]>x)return i;} void reset(){ten[0]=1;rep(i,1,11)ten[i]=ten[i-1]*10;} void force(int x,int tot,LL tmp) { tim++; if(x==tot+1){/*cout<<"+"<<endl;*/luck[++cnt]=tmp;return;} force(x+1,tot,tmp+6*ten[x]),force(x+1,tot,tmp+8*ten[x]); } LL gcd(LL x,LL y) { if(x>y)swap(x,y); if(x==0)return y; return gcd(y%x,x); } void search(int x,int step,LL lcm) { if(lcm>b)return; if(step==cnt){if(!x)return;ans+=(x&1?1:-1)*(((b/lcm)-(a+lcm-1)/lcm)+1);return;} search(x,step+1,lcm); LL tmp=lcm/gcd(lcm,luck[step+1]); if(1.0*tmp*luck[step+1]<=b) search(x+1,step+1,tmp*luck[step+1]); } bool cmp(LL x,LL y){return x>y;} int main() { memset(inf,0x7f,sizeof(inf)); reset(); a=read(),b=read(); lenb=getten(b); rep(i,0,lenb) force(0,i,0); // cout<<tim<<endl; sort(luck+1,luck+cnt+1,cmp); rep(i,1,cnt) if(luck[i]!=-inf[0]) rep(j,i+1,cnt) if(luck[j]!=-inf[0]&&luck[i]%luck[j]==0)luck[i]=-inf[0]; luck[cnt+1]=-inf[0]; sort(luck+1,luck+cnt+1,cmp); // rep(i,1,cnt)cout<<luck[i]<<endl; cnt=0; while(luck[cnt+1]!=-inf[0])cnt++; // cout<<"shing"<<endl; search(0,0,1); write(ans); return 0; } //1 10000000000
并不对劲的暴力选手表示写完后身心愉悦。