7/17 D 题题解

· · 个人记录

题目大意

给出一个 N\times M 的网格,可以从 (1,1) 向网格中的某个整点发出一条光线,光线撞到四壁会反弹,当光线射入四角之一时结束,求对于每一个终点,可能的光线轨迹的数量。

## 题解 下面为了方便认为网格左上角是 $(0,0)$,右下角是 $(n,m)$。 枚举互质的 $i,j$,我们考虑它的终点。有一个著名 trick,把反射光按镜面对称后可以与入射光拼成一条直线,所以你可以想象一排无限对称复制的网格: $$\begin{matrix}A&B&A&B&...\\C&D&C&D&...\\A&B&A&B&...\\C&D&C&D&...\\\vdots&\vdots&\vdots&\vdots&\ddots\end{matrix}$$ 假设光线射到 $(an,bm)$(需要有 $\gcd(a,b)=1$,否则必然射不到)。那么它射到的是 $A,B,C,D$ 中的哪一个由 $(a\bmod 2,b\bmod 2)$ 决定。 发现有 $a=\dfrac{im}{\gcd(im,jn)},b=\dfrac{jn}{\gcd(im,jn)}$。考虑他们 $\bmod 2$ 的结果,我们发现如果设 $\text{cnt}(x)$ 是 $x$ 的质因子中 2 的数量,则有 $$\begin{cases}\texttt{射到}B&(\text{cnt}(i)+\text{cnt}(m)-\text{cnt}(j)-\text{cnt}(n)>0)\\\texttt{射到}D&(\text{cnt}(i)+\text{cnt}(m)-\text{cnt}(j)-\text{cnt}(n)=0)\\\texttt{射到}C&(\text{cnt}(i)+\text{cnt}(m)-\text{cnt}(j)-\text{cnt}(n)<0)\end{cases}$$ 注意 $A$ 是必然射不到的。 设这个函数为 $\text{calc}(i,j)$。射到 $X$ 的方案数即为 $$\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1][\text{calc}(i,j)=X]$$ (这里忽略了射向 $(0,1),(1,0)$ 的两条直线,记得特判。) 反演,同时因为 $\text{cnt}(id)=\text{cnt}(i)+\text{cnt}(d)$,所以有 $$\sum_{d=1}^{\min(n,m)}\mu(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}\sum_{j=1}^{\lfloor\frac md\rfloor}[\text{calc}(i,j)=X]$$ 杜教筛出 $\mu$ 的前缀和即可数论分块。 考虑统计后面那个东西,把 $\text{cnt}(x)=i$ 的 $x$ 塞进桶里再处理一下前后缀和即可 $\log n$ 统计。 参考代码: ```cpp #include<bits/stdc++.h> #define ll long long using namespace std; ll N,sqN,M,sqM; const int len=5000000,LEN=5000000; bool flg[len+5];int pri[len+5],cnt; ll mu[len+5],smu[len+5]; void init(){ mu[1]=1; for(int i=2;i<=len;i++){ if(!flg[i]) pri[++cnt]=i,mu[i]=-1; for(int j=1;j<=cnt&&i*pri[j]<=len;j++){ flg[i*pri[j]]=1; if(i%pri[j]) mu[i*pri[j]]=-mu[i]; else {mu[i*pri[j]]=0;break;} } } for(int i=1;i<=len;i++) smu[i]=smu[i-1]+mu[i]; } bool visN[LEN];ll ans_muN[LEN]; ll inline ret_muN(ll x){ if(x<=sqN) return smu[x]; return ans_muN[N/x]; } void SolveN(ll x){ if(x<=sqN) return; if(visN[N/x]) return; visN[N/x]=1; ans_muN[N/x]=1; for(ll l=2,r;l<=x;l=r+1){ r=(x/(x/l)); SolveN(x/l); ans_muN[N/x]-=(r-l+1)*ret_muN(x/l); } } bool visM[LEN];ll ans_muM[LEN]; ll inline ret_muM(ll x){ if(x<=sqM) return smu[x]; return ans_muM[M/x]; } void SolveM(ll x){ if(x<=sqM) return; if(visM[M/x]) return; visM[M/x]=1; ans_muM[M/x]=1; for(ll l=2,r;l<=x;l=r+1){ r=(x/(x/l)); SolveM(x/l); ans_muM[M/x]-=(r-l+1)*ret_muM(x/l); } } int cntn,cntm; int ans[4]; const int p=998244353; ll valN[50],valM[50]; ll SvalN[50],SvalM[50]; ll TvalN[50],TvalM[50]; int main(){ init(); scanf("%lld%lld",&N,&M); N--;M--;sqN=pow(N,2.0/3.0);sqM=pow(M,2.0/3.0); memset(visN,0,sizeof(visN)); SolveN(N); memset(visM,0,sizeof(visM)); SolveM(M); ll N1=N;while(N1%2==0) cntn++,N1/=2; ll M1=M;while(M1%2==0) cntm++,M1/=2; for(ll l=1,r,lstans=0,nowans;l<=N&&l<=M;l=r+1){ if(N/(N/l)<M/(M/l)) r=N/(N/l),nowans=ret_muN(r); else r=M/(M/l),nowans=ret_muM(r); for(ll i=48;i>=0;i--) valN[i]=((N/l)>>i)-((N/l)>>(i+1)), valM[i]=((M/l)>>i)-((M/l)>>(i+1)); SvalM[0]=valM[0]; for(int i=1;i<=48;i++) SvalM[i]=SvalM[i-1]+valM[i]; TvalM[48]=valM[48]; for(int i=47;i>=0;i--) TvalM[i]=TvalM[i+1]+valM[i]; for(int i=0;i<=48;i++){ if(i+cntm-cntn>=0&&i+cntm-cntn<=48) ans[3]=(ans[3]+1LL*((nowans-lstans)%p+p)%p*(valN[i]%p)%p*(valM[i+cntm-cntn]%p)%p)%p; if(i+cntm-cntn>0) if(i+cntm-cntn>48) ans[2]=(ans[2]+1LL*((nowans-lstans)%p+p)%p*(valN[i]%p)%p*(SvalM[48]%p)%p)%p; else ans[2]=(ans[2]+1LL*((nowans-lstans)%p+p)%p*(valN[i]%p)%p*(SvalM[i+cntm-cntn-1]%p)%p)%p; if(i+cntm-cntn<48) if(i+cntm-cntn<0) ans[1]=(ans[1]+1LL*((nowans-lstans)%p+p)%p*(valN[i]%p)%p*(TvalM[0]%p)%p)%p; else ans[1]=(ans[1]+1LL*((nowans-lstans)%p+p)%p*(valN[i]%p)%p*(TvalM[i+cntm-cntn+1]%p)%p)%p; } lstans=nowans; } printf("0\n%d\n%d\n%d\n",ans[1]+1,ans[2]+1,ans[3]); return 0; } ```