几道数学题

从给定的[a,b]区间选出一个数x,从[c,d]区间选出数y,问有多少对(x,y)满足(x+y) % p = m

思路:对于a<=x<=b,c<=y<=d.

满足条件的结果为ans=f(b,d)-f(b,c-1)-f(a-1,d)+f(a-1,c-1)。

而函数f(a,b)是计算0<=x<=a,0<=y<=b满足条件的结果。这样计算就很方便了。

 

LL   gcd(LL x , LL y){
     return y==0 ? x : gcd(y , x%y) ;
}
 
LL   p , m ;
LL   ans(LLa , LL b){
     if(a < 0 || b < 0) return0 ;
     LL s = 0  ;
     LL ma = a % p , mb = b % p ;
     s += (a/p)*(b/p) * p ;
     s += (ma+1)*(b/p) + (mb+1)*(a/p) ;
     if(ma > m){
          s += min(m , mb) + 1 ;
          LL t = (p+m-ma) % p ;
         if(t <= mb)s += mb-t+1 ;
     }
     else{
          LL t = (p+m-ma) % p ;
          if(t <= mb) s += min(mb-t+1 , m-t+1) ;
     }
     return s ;
}
 
int  main(){
     int t , T = 1 ;
     LL a , b , c , d , s , g , f ;
     cin>>t ;
     while(t--){
         cin>>a>>b>>c>>d>>p>>m ;
          s = ans(b,d) - ans(c-1 , b) - ans(a-1,d) + ans(a-1 , c-1) ;
          f = (d-c+1) * (b-a+1) ;
          g = gcd(f , s) ;
          printf("Case #%d: %I64d/%I64d\n" , T++ , s/g , f/g) ;
     }
     return 0 ;
}
 


路径f:由A到D;g:由B到C的路径。 沿坐标轴正方向走,f,g不能有交集。求路径对(f,g)总数

A(0,0),B(p,0),C(m,q),D(m,n);
因为(f,g)不相交数=(f,g)总数-(f,g)相交数;
总数:  c(m+n,m)*c(m-p+q,q);
相交数:c(m+q,m)*c(m-p+n,n);

 
const  LL  mod = 100000007 ;
LL exgcd(LL a ,LL b ,LL &x ,LL &y){
    if(b==0){
        x=1 ;
        y=0 ;
        return a ;
    }
    LL g=exgcd(b,a%b,x,y) ;
    LL t=x ;
    x=y ;
    y=t-(a/b)*y ;
    return g ;
}
 
LL   inverse(LL n){
     LL x , y ;
     exgcd(n , mod , x , y) ;
     return (x % mod  + mod) % mod ;
}
 
LL   C(LL n , LL m){
     LL u = 1 , v = 1 ;
     for(LL i = 0 ; i < m ; i++){
          u = u * (n-i) % mod ;
          v = v * (i+1) % mod ;
     }
     return u * inverse(v) % mod ;
}
 
int  main(){
     LL  s , m , n , p  , q  ;
     while(cin>>m>>n>>p>>q){
          s = C(m+n , n) * C(m+q-p , q) % mod ;
          s -= C(m+q,m) * C(m+n-p,n) % mod ;
          s = (s % mod + mod) % mod ;
          cout<<s<<endl ;
     }
     return 0 ;
}
 


要求满足的m=a^3+b^3=(a+b)(a2-ab+b2)的(a,b)组合。

t=x1+x2,t一定是m的约数,所以应枚举m的所有约数。

然后可以得到

t = x1+x2

n/t = x1^2 - x1*x2 +x2^2 ;

     = t*t – 3x1*x2

ð   x1*x2 =(t*t-n/t) / 3  = m ;

有韦达定理  x1,x2为二次方程 x^2 – mx + t = 02正整数根 

再就是注意范围要用unsigned long long

typedef unsigned long long ll ;
const ll  M = 3000001 ;
int cnt,num,cn;
ll  prime[M],p[100],e[100];
bool f[M];
ll n;
void make(){
    cnt=0;
    for(int i=2;i<M;i++){
        if(!f[i]) prime[cnt++]=i;
        for(int j=0;j<cnt&&i*prime[j]<M;j++){
            f[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
}
void fac(ll m){
    num=0;
    for(ll i=0;i<cnt&&(ll)prime[i]*prime[i]<=m;i++){
        if(m%prime[i]==0){
            p[num]=prime[i];
            ll j=1;
            m/=prime[i];
            while(m%prime[i]==0){
                j++;
                m/=prime[i];
            }
            e[num++]=j;
        }
    }
    if(m>1){
        p[num]=m;
        e[num++]=1;
    }
}
ll ispw2(ll a){
    ll b=sqrt(1.0*a);
    if((ll)b*b==a) return b;
    return M;
}
 
map<ll , ll> answer ;
map<ll , ll> ::iterator it ;
void is(ll t){
    ll x1,x2;
    ll p=n/t;
    ll a=t*t-p;
    if(a>0&&a%3!=0) return ;
    ll m=a/3;
    ll b=t*t-4*m;
    if(b<0) return ;
    ll c=ispw2(b);
    if(c==M) return ;
    if((t+c)%2==0){
        x1=(t+c)/2;
        x2=t-x1;
        if(x1>x2) swap(x1,x2);
        if(x1>0&&x1<t&&x2>0&&x2<t&&answer.find(x1) == answer.end()){
            answer[x1] = x2 ;
        }
    }
}
 
ll  Pow(ll x,ll y){
    ll s=1;
    for(;y;y>>=1 , x*=x){
        if(y&1) s*=x;
    }
    return s;
}
 
void dfs(int id,ll s){
    is(s);
    if(id>=num) return;
    for(int i=0;i<=e[id];i++)
        dfs(id+1, (ll)s*Pow(p[id],i));
}
 
int main(){
    make();
    while(scanf("%llu",&n)!=EOF){
        fac(n);
        cn=0;
        answer.clear() ;
        dfs(0,1);
        printf("%d",answer.size()) ;
        for(it = answer.begin() ;it != answer.end() ; it++)
            printf(" (%llu,%llu)",it->first,it->second);
        printf("\n");
    }
    return 0;
}
 


一门课的书有 N 章内容要复习,每天可以复习一章,但是有 M 个限制条件:在第 D[i] 天中无法复习第 C[i] 章,问不同的复习方案数

 

容斥原理,不考虑约束条件,则一共有 N! 种复习方案,然后减去刚好触发一个约束条件的方案数,加上刚好触发两个约束条件的方案数,减去刚好触发三个的……依此类推

另外注意一点,输入中可能包含重复的约束条件,要先去重再算容斥,否则会得到错误的结果

题意抽象出来就是 在  n*n 的 棋盘中 加了 m 个禁位, 放置 n 个棋子 ,每两个棋子不在同一行 同一列,问有多少中放置方式 ?

trick  :

    M 个禁位 中 有相同 坐标的点。

解法 : 利用 有禁位的排列的公式 (容斥原理):  

n! - r1 *( n-1)! + r2*(n -2)! - r3*(n-3)! +..........

ri  指 在禁区中 选 i 个 位置的 方案数。

由于 m 的值较小 ,可直接 dfs暴力求方案数。


const LL mod = 55566677 ;
int   x[28] , y[28] ;
LL    p[60] ;
bool  visx[60] , visy[60] ;
int   n , m ;
LL    s ;
 
void  dfs(int cnt , int id , int f){
      s = (s + p[n-cnt]*f) % mod ;
      s = (s+mod) % mod ;
      for(int i = id+1 ; i < m ; i++){
           if(!visx[x[i]] && !visy[y[i]]){
               visx[x[i]] = visy[y[i]] = 1 ;
               dfs(cnt+1 , i , -f) ;
               visx[x[i]] = visy[y[i]] = 0 ;
           }
      }
}
 
int   main(){
      int i , j ;
      p[0] = p[1] = 1 ;
      for(i = 2 ; i < 60 ; i++) p[i] =  p[i-1] * i % mod ;
      while(cin>>n>>m){
           for(i = 0 ; i < m ; i++) scanf("%d%d" ,&x[i] , &y[i]) ;
           for(i = 0 ; i < m ; i++){
               for(j = i+1 ; j < m ; j++){
                    if(x[i]==x[j] && y[i]==y[j]){
                        swap(x[j] , x[m-1]) ;
                        swap(y[j] , y[--m]) ;
                    }
               }
           }
           memset(visx , 0 , sizeof(visx)) ;
           memset(visy , 0 , sizeof(visy)) ;
           s = 0 ;
           for(i = 0 ; i < m ; i++){
               visx[x[i]] = visy[y[i]] = 1 ;
               dfs(1 , i , 1) ;
               visx[x[i]] = visy[y[i]] = 0 ;
           }
           s = (p[n] - s) % mod ;
           s = (s + mod) % mod;
           cout<<s<<endl ;
      }
      return 0 ;
}


限位排列

一门课的书有 N 章内容要复习,每天可以复习一章,但是第 i 章不能在第 i 天或第 i + 1 天复习(最后一章则对应最后一天和第一天),问不同的复习方案数, 递推公式如下:

int main(){
    f[0]=f[3]=1, f[1]=f[2]=0, f[4]=2;
    int n ;
    for(n = 5 ; n <= maxn ; n++){
        if(n%2)
            f[n] = (f[n-1]*n%mod + (f[n-2]*n%mod + 4) * inverse(n-2)) % mod ;
        else
            f[n] = (f[n-1]*n%mod + (f[n-2]*n%mod - 4) * inverse(n-2)) % mod ;
    }
    while(scanf("%d" ,&n) != EOF){
         printf("%lld\n" , f[n]) ;
    }
    return 0;
}
 


题意:求第k个斐波那契素数对应的斐波那契数列及其之后的值%x==0的最小的斐波那契数值,(%m), zoj
数据范围:(1<=k<=10^6),(3<=X<=100),(10<=M<=10^6)
解题思路:
斐波那契数列的特性:( 0,1,1,2,3,5,8... )
1
gcd(fib(n),fib(m))=fib(gcd(n,m))
2
、如果fib(k)能被x整除,则fib(k*i)都可以被x整除。
3
f(0)+f(1)+f(2)+…+f(n)=f(n+2)-1
4
f(1)+f(3)+f(5)+…+f(2n-1)=f(2n)
5
f(2)+f(4)+f(6)+…+f(2n) =f(2n+1)-1
6
[f(0)]^2+[f(1)]^2+…+[f(n)]^2=f(n)·f(n+1)
7
f(0)-f(1)+f(2)-…+(-1)^n·f(n)=(-1)^n·[f(n+1)-f(n)]+1
8
f(m+n)=f(m-1)·f(n-1)+f(m)·f(n)
9
[f(n)]^2=(-1)^(n-1)+f(n-1)·f(n+1)
10
f(2n-1)=[f(n)]^2-[f(n-2)]^2
11
3f(n)=f(n+2)+f(n-2)
12
f(2n-2m-2)[f(2n)+f(2n+2)]=f(2m+2)+f(4n-2m)[ nm≥-1,n≥1]
还有一个结论:
计算(a/b)%c  其中b能整除a
如果bc互素,则(a/b)%c=a*b^(phi(c)-1)%c
如果bc不互素,则(a/b)%c=(a%bc)/b
对于bc互素和不互素都有(a/b)%c=(a%bc)/b成立


斐波那契素数的定义:( 2,3,5,13,89... )
对于斐波那契数列的第i个元素s[i],如果gcd(s[i],s[j])==1(1<=j<i),那么s[i]是一个斐波那契素数。


斐波那契素数的特性(可根据斐波那契数列求出斐波那契素数)
对于斐波那契数列(0,1,1,2,3,5,8...),下标从0开始,下标为3,4,5,7,11,13...的数就是斐波那契素数。
即除了前两个外,斐波那契数列的素数下标对应的就是斐波那契素数,而如果下标不是素数,那么对应的也不是斐波那契素数。


所以这一题就先筛出斐波那契素数下标(3,4,5,7,11,13...),然后就可以知道第k个斐波那契素数对应的斐波那契数列的下标,

根据矩阵快速幂计算出斐波那契数值,判断是否%x==0,如果不是就继续找下一个,直到找到为止。最后a/x%m=(a%(x*m))/x 

 

 

 
typedef long long LL;
const  int  maxn =  15500000  ;
int    primesize ;
int    prime[maxn + 8] ;
bool   is[maxn + 8] ;
void   make(){
       primesize = 0  ;
       memset(is , 0 , sizeof(is)) ;
       for(int i = 2 ; i <= maxn ; i++){
           if(! is[i]) prime[++primesize] = i ;
           for(int j = 1 ; j <= primesize && prime[j]*i <= maxn ; j++){
                 is[prime[j]*i] = 1 ;
                 if(i % prime[j] == 0) break  ;
           }
       }
       prime[1] = 3 ;
       prime[2] = 4 ;
}
 
LL    mod  ;
struct Mat{
       LL x[3][3] ;
       Mat(){
           x[1][1] = 0 , x[1][2] = 0 ;
           x[2][1] = 0 , x[2][2] = 0 ;
       }
       Mat(int){
           x[1][1] = 1 , x[1][2] = 0 ;
           x[2][1] = 0 , x[2][2] = 1 ;
       }
       Mat(LL a11 , LL a12 , LL a21 , LL a22){
           x[1][1] = a11 , x[1][2] = a12 ;
           x[2][1] = a21 , x[2][2] = a22 ;
       }
};
 
Mat operator * (Mat A , Mat B){
     Mat s ;
     for(int i = 1 ; i <= 2 ; i++){
        for(int j = 1 ; j <= 2 ; j++){
            for(int k = 1 ; k <= 2 ; k++){
                 s.x[i][j] += A.x[i][k] * B.x[k][j] ;
                 s.x[i][j] %= mod ;
            }
        }
     }
     return s ;
}
 
Mat operator ^ (Mat x , int y){
    Mat s(1) ;
    for(; y ; y>>=1){
        if(y&1) s = s * x ;
        x = x * x ;
    }
    return s ;
}
 
const  Mat F(1 , 1 , 1, 0) ;
 
int  main(){
     make() ;
     LL k , x , m  ;
     int i  , t  ;
     Mat A ;
     cin>>t  ;
     while(t--){
          scanf("%lld%lld%lld" ,&k  , &x , &m)  ;
          int id = prime[k] ;
          mod = x ;
          for(i = id ;  ; i++){
               A = F ^ (i-1) ;
               if(A.x[1][1] % x == 0) break ;
          }
          mod = x * m ;
          A = F ^ (i-1) ;
          printf("%lld\n" , A.x[1][1]/x) ;
     }
     return 0 ;
}
 

关于方程a^x=1(modm)的最小x解

定理:

gcd(a,m)=1,必有正整数x,使得a^x=1(mod m),且设满足等式的最小正整数为x0,必满足x0|phim.注意m>1.

否则如果gcd(a,m)!=1,则方程a^x=1(modm)没有解。

const int maxn = 70000 ;
int   prime[maxn] ;
int   primesize ;
bool  is[maxn+10] ;
void  make(){
      memset(is , 0  , sizeof(is)) ;
      primesize = 0 ;
      for(int i = 2 ; i <= maxn ; i++){
          if(! is[i]) prime[primesize++] = i ;
          for(int j = 0 ; j < primesize && i*prime[j] <= maxn ; j++){
               is[i*prime[j]] = 1 ;
               if(i % prime[j] == 0) break ;
          }
      }
}

int   phi(int n){
      int m = sqrt(0.5 + n) ;
      int s = n ;
      for(int i = 0 ; i < primesize && n!=1 && prime[i] <= m ; i++){
            if(n % prime[i] == 0){
                 s = s - s/prime[i] ;
                 while(n % prime[i] == 0)  n/=prime[i] ;
            }
      }
      if(n != 1) s = s - s/n ;
      return s ;
}

int   gcd(int x , int y){
      return y == 0 ? x : gcd(y , x % y) ;
}

struct Fac{
       int x ;
       int s ;
       Fac(){}
       Fac(int _x , int _s):x(_x),s(_s){}
}h[208] ;
vector<int>  factor ;
vector<int>  ::iterator it ;
int   facsize ;
void  getfac(int n){
      int m = sqrt(0.5 + n)  , t ;
      facsize = 0 ;
      for(int i = 0 ; i < primesize && n != 1 && prime[i] <= m ; i++){
           if(n % prime[i] == 0){
                t = 0 ;
                while(n % prime[i] == 0){t++ ; n/=prime[i] ;}
                h[facsize++] = Fac(prime[i] , t) ;
           }
      }
      if(n != 1) h[facsize++] = Fac(n , 1) ;
}

int  Pow(int x , int y){
     int s = 1 ;
     for(;y;y>>=1 , x *= x){
          if(y&1) s *= x ;
     }
     return s ;
}

int  Pow(int x , int y , int m){
     int s = 1 ;
     for(;y;y>>=1 , x *= x , x %= m){
          if(y&1) s *= x , s %= m ;
     }
     return s ;
}

void  dfs(int id , int n){
      if(id == facsize){
           factor.push_back(n) ; return ;
      }
      for(int i = 0 ; i <= h[id].s ; i++)
          dfs(id+1 , n*Pow(h[id].x , i)) ;
}

void  getfactor(int n){
      factor.clear() ;
      getfac(n) ;
      dfs(0 , 1) ;
      sort(factor.begin() , factor.end()) ;
}

int   answer(int a , int m){
      getfactor(phi(m)) ;
      for(it = factor.begin() ; it != factor.end() ; it++){
            if(Pow(a , *it , m) == 1) return *it ;
      }
}

int   main(){
      make() ;
      int n  , i  ,  ph ;
      while(scanf("%d" ,&n) != EOF){
           if(n == 1 || gcd(2 , n) != 1){
                printf("2^? mod %d = 1\n" , n) ;
                continue ;
           }
           printf("2^%d mod %d = 1\n" , answer(2 , n) , n ) ;
      }
      return 0 ;
}


写法2

LL    phi(LL n){
      LL s = n ;
      LL m = (LL)sqrt(0.5 + n) ;
      for(LL i = 2 ; n!=1 && i <= m ; i++){
            if(n % i == 0){
                 s = s - s/i ;
                 while(n % i == 0)  n /= i ;
            }
      }
      if(n != 1) s = s - s/n ;
      return s ;
}

LL   gcd(LL x , LL y){
     return y == 0 ? x : gcd(y , x % y) ;
}

vector<LL>  factor ;
vector<LL>  ::iterator it ;

void  getfactor(LL n){
      factor.clear() ;
      LL m = (LL)sqrt(0.5 + n) ;
      for(LL i = 1 ; i <= m ; i++){
            if(n % i == 0){
                 factor.push_back(i) ;
                 if(i * i != n) factor.push_back(n/i) ;
            }
      }
      sort(factor.begin() , factor.end()) ;
}

LL   Pow(LL x , LL y , LL m){
     LL s = 1 ;
     for(;y;y>>=1 , x *= x , x %= m){
          if(y&1) s *= x , s %= m ;
     }
     return s ;
}

LL   answer(LL a , LL m){
     getfactor(phi(m)) ;
     for(it = factor.begin() ; it != factor.end() ; it++){
            if(Pow(a , *it , m) == 1) return *it ;
     }
}

int   main(){
      LL  n  , i   ;
      while(scanf("%I64d" ,&n) != EOF){
           if(n == 1 || gcd(2 , n) != 1){
                printf("2^? mod %lld = 1\n" , n) ;
                continue ;
           }
           printf("2^%I64d mod %I64d = 1\n" , answer(2 , n) , n ) ;
      }
      return 0 ;
}


题目大意是:在一个平面上有N个点,每个点的坐标已经给出,现在要求在X轴上找一个点,使得这个点到所有点中最大的距离最小。

         分析:我们设这个点为X0,所求的距离为F(x),那么对于所有的 X < X0 和 X > X0 都有F(x) > ans,即实际上这个函数是已X0为最小值,两边

都是单调递增或者递减的,因此我们可以三分查找找出这个最值的坐标。总复杂度O(n*logn). 

const double EPS=1e-9;

const   int maxn = 50008 ;
int     n  ;
double  x[maxn] , y[maxn] ;

double f(double nx){
       double s  = 0  , d ;
       for(int i = 1 ; i <= n ; i++){
            d = sqrt(y[i]*y[i] + (x[i]-nx)*(x[i]-nx)) ;
            if(s < d) s = d  ;
       }
       return s ;
}
//三分 对凹(凸)函数判断最小(大)值,此题是求最小值。
//不要求左l右r值的大小比较,即(L<R 或 L>=R)都可
double tri_search(double l , double r){
    double Mid , Midmid , L ,R ;
    L = l ;
    R = r ;
    while( L + EPS < R ){ // 由于L ,R没有要求谁大谁小,故求的绝对值
        Mid=(L+R)*0.5;
        Midmid=(Mid+R)*0.5;
        if(f(Mid)<= f(Midmid)  )
                R=Midmid;
        else
            L=Mid;
    }
    return (L+R)*0.5;
}

int  main(){
    int  i ;
    double  l , r  , nx ;
    while(cin>>n && n){
        l =   200008  ;
        r =   - 200008 ;
        for(i = 1 ; i <= n ; i++){
            scanf("%lf%lf",&x[i] ,&y[i]) ;
            l = min(l , x[i]) ;
            r = max(r , x[i]) ;
        }
        nx = tri_search(l,r) ;
        printf("%.9lf %.9lf\n" , nx , f(nx)) ;
     }
     return 0 ;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值