HDOJ 4790

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值