![在这里插入图片描述]()
首先要进行一些特判:
-
当 m=1 或 n=1 或 m>n 时,结果为 0
-
当 m=n 或 m=n−1 时,结果为 1
此时 2≤m≤n−2,对 i<j 和 i≥j 这两种情况分别进行讨论:
- i<j 时,原式子变成
⌈ij⌉=m
由于 ⌈ij⌉=⌊ij−1⌋+1,因此上式等价于:
⌊ij−1⌋+1=m
再代入 i+j=n 得:
⌊in−i−1⌋+1⌊in−1⌋=m=m(1)
因此转化为 i<j 时,有多少个 i 满足式 (1) 。
- i≥j 时,原式子变成
⌊ji⌋⌊jn−j⌋⌊jn⌋=m−1=m−1=m(2)
因此转化为 i>j 时,有多少个 j 满足式 (2)。
然后可以推出各自的范围:
i<j 时:
⌊in−1⌋miimm+1ii=m≤in−1≤mn−1≤⌊mn−1⌋ 因为 i 是整数>in−1−1>in−1>m+1n−1≥⌊m+1n−1⌋+1
i≥j 时:
⌊jn⌋mjjmm+1jj=m≤jn≤mn≤⌊mn⌋>jn−1>jn>m+1n≥⌊m+1n⌋+1
因此得到两个不等式组:
⎩⎪⎨⎪⎧ii≤⌊mn−1⌋≥⌊m+1n−1⌋+1⎩⎨⎧jj≤⌊mn⌋≥⌊m+1n⌋+1
因此最终的答案就是
⌊mn−1⌋−⌊m+1n−1⌋+⌊mn⌋−⌊m+1n⌋
代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int T;
ll n,m;
int main(){
#ifdef WINE
freopen("data.in","r",stdin);
#endif
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&n,&m);
ll res=0;
if(m==1||m>n||n==1)res=0;
else if(m==n||m==n-1)res=1;
else res=(n-1)/m-(n-1)/(m+1)+n/m-n/(m+1);
printf("%lld\n",res);
}
return 0;
}
当时我是用分块做的:
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int T;
ll n,m;
int main(){
#ifdef WINE
freopen("data.in","r",stdin);
#endif
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&n,&m);
ll res=0;
if(m==1||m>n||n==1)res=0;
else if(m==n||m==n-1)res=1;
else{
ll k;
ll i=(n-1)/(m+1);
for(;i<=(n-1)/2;i=k+1){
k=(n-1)/((n-1)/i);
if((n-1)/i==m){
res+=k-i+1;
break;
}
}
ll j=n/(m+1);
for(;j<=(n-1)/2;j=k+1){
k=n/(n/j);
if(n/j==m){
res+=k-j+1;
break;
}
}
}
printf("%lld\n",res);
}
return 0;
}