【HAOI2011】Problem b

cdcq

2018-05-27 17:42:25

Personal

学高考一段时间后我的OI实力居然越来越强了 原题: 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。 100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000 (我的推导似乎和大部分人的不太一样 这个是距离高考还有10天写的…… 因为快高考了所以放飞自我,自习课不想复习拿反演玩一玩,发现这个挺简单的啊,以前怎么学不会呢 大概是学高考增强了我的推公式的能力吧 啊不扯了,直接开始题解 首先必须要想到的思路是四项容斥简化问题 问题转化为求有对少个数对$ (x,y) $满足$ 1\leq x \leq n, 1\leq y \leq m $且$ gcd(x,y)=1 $ 看过其他反演题的话可能容易想到,$ \sum \sum [gcd(i,j)=1] $是比较好算的 那么可以把n和m都除等于k,问题就转化为求$ \displaystyle\sum_{i=1}^{n} \sum_{j=1}^{m} [gcd(i,j)=1] $ 因为考虑到对于原来的满足条件的一个数对$ (i,j) $,因为i和j的gcd是k,所以可以写成这样$ (i'\cdot k,j'\cdot k) $,并且i'和j'的gcd是1 显然若$ i'\cdot k\leq n $,则$ i'\leq \lfloor \frac{n}{k} \rfloor$,j'同理 现在的问题已经相对简单,剩下的就是推推推了~ $$ ans(n,m)=\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=1] $$ $$ =\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|gcd(i,j)}\mu(d) $$ 这里应用到了 $$ \sum_{d|n}\mu(d)=\begin{cases}0& n=1\\ 1& n\neq1 \end{cases} $$ 的性质 因为$ d|gcd(i,j) $这个表现形式不好,既不利于继续化简,亦不利于代码实现,所以现在换一种表示方式 $$ ans(n,m)=\sum_{d}^{min(n,m)}\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}\mu(d) $$ 先枚举所有可能的gcd,再枚举所有因子有这个gcd的i和j,不难想象和先枚举ij再枚举d所枚举到的东西是一样的,这个似乎是莫比乌斯反演里常用的转化方法 $$ ans(n,m)=\sum_{d}^{min(n,m)}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}1 $$ 因为d和ij的枚举情况无关所以可以提到前面来 后面的东西就可以秒算了 $$ ans(n,m)=\sum_{d}^{min(n,m)}\mu(d)\cdot \lfloor \frac{n}{d} \rfloor \cdot \lfloor \frac{m}{d}\rfloor $$ 至此公式推导完毕 我的推导和popoqqq的不太一样,他是构造了f和F并使用反演的公式,我是根据反演的性质来转化问题,现在看来两种方法本质是一样的 我的思路受《crash的表格》这道题的影响比较深 直接$ O(n) $计算会T(因为有n组询问,需要用莫比乌斯反演常用的$ O(\sqrt{n}) $求答案的计算方式,这个在po姐的ppt里解释得很清楚,不再赘述 代码: ```cpp #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; int rd(){int z=0,mk=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} return z*mk; } int n,m,n1,n2,m1,m2; int mu[51000],mus[51000]; //mu's!!!!!! int prm[51000],prt=0; bool prg[51000]; void gtmu(){ memset(prg,0,sizeof(prg)); mu[1]=mus[1]=1; for(int i=2;i<=50000;++i){ if(!prg[i]) prm[++prt]=i,mu[i]=-1; for(int j=1;prm[j]*i<=50000 && j<=prt;++j){ prg[prm[j]*i]=true; if(!(i%prm[j])){ mu[i*prm[j]]=0; break;} mu[prm[j]*i]=-mu[i]; } mus[i]=mus[i-1]+mu[i]; } } int sm(int x,int y){ return (x+1)*x*(y+1)*y/4;} int cclt(int x,int y){ if(x>y) swap(x,y); int tmp,bwl=0; for(int i=1;i<=x;i=tmp+1){ tmp=min(x/(x/i),y/(y/i)); //bwl+=sm(x/i,y/i)*(mus[tmp]-mus[i-1]); bwl+=(x/i)*(y/i)*(mus[tmp]-mus[i-1]); } return bwl; } int main(){freopen("ddd.in","r",stdin); gtmu(); cin>>n; while(n --> 0){ //趋向于 //cin>>n1>>n2>>m1>>m2>>m; n1=rd(),n2=rd(),m1=rd(),m2=rd(),m=rd(); printf("%d\n",cclt(n2/m,m2/m)+cclt((n1-1)/m,(m1-1)/m)-cclt((n1-1)/m,m2/m)-cclt(n2/m,(m1-1)/m)); //注意-1 } return 0; } ```