1、用数形结合的方法做。
摘抄某神犇的题解:
这题就是要找在[a,b] [c,d] 之间,和模p等于m的对数。
把[a,b] [c,d]所有可能组合的和写成下列形式。
a+c a+c+1 a+c+2 ..................a+d
a+c+1 a+c+2 a+c+3 ........a+d a+d+1
a+c+2 a+c+3 a+d a+d+1 a+d+2
....................
...................
b+c b+c+1 ...............................................b+d;
上面大致形成一个斜的矩阵。
使用b+c 和 a+d两条竖线,就可以分成三部分。前后两部分个数是等差数列,中间个数是相等的。
只需要讨论下b+c 和 a+d的大小。 然后找到%p==m 的位置,求和就可以搞定了。
2、有十个关键点,分别是:四个分界点,每两个分界点之间的最靠近分界点的两个点。一共4+2*3=10个点。
3、分界点不落在范围内的话,高度(每一列的元素数)算作0。分界点处的高度要单独统计,因为要避免重复。
4、两边用等差数列求和公式做,中间是一个矩形,求面积即可。
5、虽然程序看起来很繁琐,但是思路比较简单。四个分界点用x0到x3表示,剩下的就是夹在分界点中间的关键点了。一次AC,但是写了很久。。。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct{
int index;
int num;
}x[10];
long long gcd(long long a,long long b){
if(b==0) return a;
return gcd(b,a%b);
}
int main(){
int T,kase=0;
long long a,b,c,d,p,m,mod,minus,tot;
scanf("%d",&T);
while(T--){
memset(x,0,sizeof(x));
scanf("%I64d%I64d%I64d%I64d%I64d%I64d",&a,&b,&c,&d,&p,&m);
if(d-c<b-a) {swap(a,c);swap(b,d);}
x[0].index=0;x[0].num=1;
x[1].index=b-a;x[1].num=b-a+1;
x[2].index=d-c;x[2].num=b-a+1;
x[3].index=b+d-a-c;x[3].num=1;
for(int i=0;i<=2;i++){
mod=(a+c)%p;
mod=(mod+x[i].index)%p;
if(m>mod){
minus=m-mod;
}else{
minus=m-mod+p;
}
x[i+4].index=x[i].index+minus;
if(i==0) x[i+4].num=x[i].num+minus;
if(i==1) x[i+4].num=x[i].num;
if(i==2) x[i+4].num=x[i].num-minus;
}
for(int i=1;i<=3;i++){
mod=(a+c)%p;
mod=(mod+x[i].index)%p;
if(m<mod){
minus=mod-m;
}else{
minus=p-m+mod;
}
x[i+6].index=x[i].index-minus;
if(i==1) x[i+6].num=x[i].num-minus;
if(i==2) x[i+6].num=x[i].num;
if(i==3) x[i+6].num=x[i].num+minus;
}
long long ans=0,temp;
temp=(x[7].index-x[4].index)/p+1;
if(temp<0) temp=0;
if(x[7].index<=x[0].index||x[4].index>=x[1].index) temp=0;
ans+=(x[4].num+x[7].num)*temp/2;
temp=(x[8].index-x[5].index)/p+1;
if(temp<0) temp=0;
if(x[8].index<=x[1].index||x[5].index>=x[2].index) temp=0;
ans+=(b-a+1)*temp;
temp=(x[9].index-x[6].index)/p+1;
if(temp<0) temp=0;
if(x[9].index<=x[2].index||x[6].index>=x[3].index) temp=0;
ans+=(x[6].num+x[9].num)*temp/2;
tot=(b-a+1)*(d-c+1);
temp=(a+c)%p;
for(int i=0;i<=3;i++)
if((i==0||x[i].index>x[i-1].index)&&(temp+x[i].index)%p==m)
ans+=x[i].num;
long long g=gcd(tot,ans);
ans/=g;tot/=g;
printf("Case #%d: %I64d/%I64d\n",++kase,ans,tot);
}
return 0;
}