ACM真的不知道是否该坚持下去了。遇到的困难不是一般的多,绩点为此付出了极大代价,整天属于睡眠不够状态,但一种热爱会推动我向前吧。。。。。。。。。
言归正传吧。
说实话学这个蛋疼得很,关于这个的定理论文很少,而且描述的十分高深。我就描述一下自己的学习心得吧。这个如果直接说的话说不清楚,我就结合例子来阐述一下吧。
两个重要的公式。
例子1:hdu5297
这题算是入门的吧:题意是删除给定数字以内,可以表示成a^b形式的数字。
#include <algorithm> #include <cstring> #include <string> #include <cmath> #include <vector> #include <map> #define clr(x, y) memset(x, y, sizeof x) using namespace std; typedef long long LL; LL n,r; int prime[30]={-2,-3,-5,-7,-11,-13,-17,-19,-23,-29,-31,-37,-41,-43,-47,-53,-59,-61,-67}; //用负数,便于容斥时判加减 vector <int> m; void init() {//得到<=r的质数的各种组合的乘积 m.clear(); for(int i=0;abs(prime[i])<=r;i++) { int si=m.size(); for(int j=0;j<si;j++) { if(abs(m[j]*prime[i])<=63) m.push_back(m[j]*prime[i]); printf("%d\n",m[j]*prime[i]); } m.push_back(prime[i]); } } LL cal(LL x) { LL sum=0; int si=m.size(); for(LL i=0;i<si;i++) { //+0.5提高精度,-1 由于1一定会减掉,所以先不计算1 LL num=(LL)pow(x+0.5,1.0/abs(m[i]))-1; if(m[i]<0) sum+=num; else sum-=num; } return x-sum-1; } LL bis() { //迭代计算,用temp暂存,避免算两次cal(num) LL num=n; LL temp=cal(num); while(temp<n) { num+=n-temp; temp=cal(num); } return num; } int main() { //freopen("input.txt","r",stdin); int T; scanf("%d",&T); while(T--) { scanf("%lld %lld",&n,&r); init(); printf("%lld\n",bis()); } return 0; }
借用一下别人家的代码。
例子2:BZOJ 2440
n次询问,每次询问有多少个数对(x,y)满足a<=x<=b,c<=y<=d且gcd(x,y)=k
N<=5W,1<=a<=b<=5W,1<=c<=d<=5W。
首先利用容斥原理将一个询问拆分成四个,每次询问有多少个数对(x,y)满足1<=x<=n,1<=y<=m且gcd(x,y)=k
这个问题等价于询问有多少个数对(x,y)满足1<=x<=floor(n/k),1<=y<=floor(m/k)且x与y互质
这题要注意最后的公式因为取值有相同的,所以用前缀数组来算莫比乌斯数值。
同样代码奉上
#include <cstdio> #include <algorithm> using namespace std; const int maxn=50010; int Prime[maxn],NotPrime[maxn],mu[maxn],sum[maxn];\\prime用来记录素数的数组,notprime用来计算非素数的数组,sum前缀莫比乌斯系数之和,mu单个的莫比乌斯系数。 int a,b,c,d,k,T; void Get_MU() { int cnt=0; mu[1]=1; for (int i=2;i<=maxn;i++) { if (!NotPrime[i]) {Prime[cnt++]=i;mu[i]=-1;} for (int j=0;j<cnt && Prime[j]*i<=maxn;j++) { NotPrime[i*Prime[j]]=1; if (i%Prime[j]==0) break; mu[i*Prime[j]]=-mu[i]; } } for (int i=1;i<=maxn;i++) sum[i]=sum[i-1]+mu[i]; } int Calc(int n,int m) { int ans=0,pos; if (n>m) swap(n,m); for (int i=1;i<=n;i=pos+1) { pos=min(n/(n/i),m/(m/i)); ans+=(sum[pos]-sum[i-1])*(n/i)*(m/i); } return ans; } int main() { Get_MU(); scanf("%d",&T); while (T--) { scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);a--;c--; a/=k;b/=k;c/=k;d/=k; printf("%d\n",Calc(b,d)+Calc(a,c)-Calc(a,d)-Calc(b,c)); } return 0; }例子3:BZOJ 2301
这题有些蛋疼需要一些数论公式的推导与证明:
点击打开链接
后续还有……(蛋疼)