zoukankan      html  css  js  c++  java
  • codeforces 487C Prefix Product Sequence (模逆元+构造)

    转自http://blog.csdn.net/houserabbit/article/details/41513745

    题解写的真棒。。

    题目链接:http://codeforces.com/problemset/problem/487/C

    题目大意:构造一个1~n的排列  使得n个前缀积对n取余是一个0~n-1的排列

    题目分析:好题,首先我们通过简单的分析可以得到n肯定是最后一个数,因为如果n在前面,前缀积肯定不止1个是n的倍数,也就是说对n取模至少有两个0,显然不满足排列,也就是说取模得到排列最后一个数是0,再来考虑前n-1个数,如果就是1 2 3 4...n-1是不是满足条件呢,显然第一个数就让它是1,是始终正确的,我们已经构造出来两个数了再来看中间的,对于一组序列a1 a2 a3 a4 ... an-1,a1=1,如果a2对n取完模要前缀积及a1*a2*a3对n取模的值与a1*a2的不同,我们不妨在a3中把a2对前缀积的影响消除,后面以此类推,这时就要用到模逆元的概念。

    对于正整数,如果有,那么把这个同余方程中的最小正整数解叫做的逆元。如果为素数,那么还可以根据费马小定理得到逆元为。推导如下:

    回到本题,我们可以构造ai=(i+1)*inv[i],(inv[i]表示i的模逆元)

    这样得到的前缀积a1a2a3...an-1=1*2*inv[1]*3*inv[2]*...*n*inv[n-1]因为inv[2]*2≡1(mod n)相当于前面说的消除了a2对a3的影响进一步分析,如果n是合数,则其必能表示成x*y,这里x,y是除了1和n的其他因子,因此对于前缀积(n-1)!必然能被n整除,这里有个特例4,4可以构造出1 3 2 4,原因很简单,因为4不算最后一项的前缀积为2*3显然6不能被4整除,但是比4大的最小的合数为6,6就不满足了,因为5!=2*3*4*5显然是6的倍数,当n不断扩大的时候,因子越来越多则更加能够被n整除,所以我们得到除了4以外的其他合数都无法构造出这样的序列,接下来就是怎样求模逆元的问题。

    根据上面的结论了,可以用费马小定理通过快速幂取模求解模逆元,不过还有更简便的递推式

    inv[i]= (n-n/i)*inv[n%i]%n,初始值inv[1]=1这个递推式的推导如下

    a=n/i

    b=n%i  =>

    a*i+b≡0(mod n) (整除部分+余数部分就等于n)  =>

    -a*i≡b(mod n) (两边同除b*i)   =>

    -a*inv[b]≡inv[i](mod n) (将a,b替换)  =>

    -(n/i)*inv[n%i]≡inv[i](mod n) (将左边变为大于0的数,直接加上n即可,因为这时nmodn=0)  =>

    (n-n/i)*inv[n%i]≡inv[i](mod n)  =>

    inv[i] = (n-n/i)*inv[n%i]%n

    下面给出两种解法:

    1.递推式:

     1 #include <cstdio>
     2 #include <cmath>
     3 #define ll long long
     4 int const MAX = 1e5 + 10;
     5 ll inv[MAX];
     6 int n;
     7 
     8 bool isprime(int x)
     9 {
    10     if(x == 1)
    11         return false;
    12     if(x == 2)
    13         return true;
    14     if(x % 2 == 0)
    15         return false;
    16     for(int i = 3; i <= sqrt(n); i += 2) 
    17         if(n % i == 0) 
    18             return false;
    19     return true;
    20 }
    21 
    22 int main() 
    23 {
    24     scanf("%d", &n);
    25     if(n == 4) 
    26     {  
    27         printf("YES
    1
    3
    2
    4
    ");  
    28         return 0;  
    29     }  
    30     else if(n == 1)
    31     {
    32         printf("YES
    1
    ");
    33         return 0;
    34     }
    35     else if(!isprime(n))
    36     {
    37         printf("NO
    ");
    38         return 0;
    39     }
    40     printf("YES
    1
    ");
    41     inv[1] = 1;
    42     for(int i = 2; i < n; i++) 
    43     {
    44         inv[i] = (n - n / i) * inv[n % i] % n;
    45         printf("%lld
    ", ((ll) i * inv[i - 1] % n));
    46     }
    47     printf("%d
    ", n);
    48 }
    代码君

    2.费马小定理+快速幂取模 (速度是第1种解法的20倍)

     1 #include <cstdio>
     2 #include <cmath>
     3 #define ll long long
     4 int const MAX = 1e5 + 10;
     5 ll inv[MAX];
     6 int n;
     7 
     8 bool isprime(int x)
     9 {
    10     if(x == 1)
    11         return false;
    12     if(x == 2)
    13         return true;
    14     if(x % 2 == 0)
    15         return false;
    16     for(int i = 3; i <= sqrt(n); i += 2) 
    17         if(n % i == 0) 
    18             return false;
    19     return true;
    20 }
    21 
    22 ll multi(ll a, ll b)  
    23 {  
    24     ll ans = 0;  
    25     a %= n;  
    26     while(b)  
    27     {  
    28         if(b & 1)  
    29         {  
    30             ans = (ans + a) % n;  
    31             b--;  
    32         }  
    33         b >>= 1;  
    34         a = (a + a) % n;  
    35     }  
    36     return ans;  
    37 } 
    38 
    39 ll quick_mod(ll a, ll b)  
    40 {  
    41     ll ans = 1;  
    42     a %= n;  
    43     while(b)  
    44     {  
    45         if(b & 1)  
    46         {  
    47             ans = multi(ans,a);  
    48             b--;  
    49         }  
    50         b >>= 1;  
    51         a = multi(a,a);  
    52     }  
    53     return ans;  
    54 }  
    55 
    56 int main() 
    57 {
    58     scanf("%d", &n);
    59     if(n == 4) 
    60     {  
    61         printf("YES
    1
    3
    2
    4
    ");  
    62         return 0;  
    63     }  
    64     else if(n == 1)
    65     {
    66         printf("YES
    1
    ");
    67         return 0;
    68     }
    69     else if(!isprime(n))
    70     {
    71         printf("NO
    ");
    72         return 0;
    73     }
    74     printf("YES
    1
    ");
    75     for(int i = 2; i < n; i++) 
    76         printf("%lld
    ", i * quick_mod(i - 1, n - 2) % n);
    77     printf("%d
    ", n);
    78 }
    View Code
  • 相关阅读:
    python模块学习第 0000 题
    报错The VMware Authorization Service is not running
    图像指纹的重复识别
    CSS预编译器配置-------LESS Sass Stylus webstorm
    CSS布局中的水平垂直居中
    进度与日程
    HTML5 application cache
    进度
    CC2530芯片介绍
    Linux命令工具 top详解
  • 原文地址:https://www.cnblogs.com/usedrosee/p/4388757.html
Copyright © 2011-2022 走看看