没例外性质。将一个数x拆成a1+a2+a3+……第i履代表i在模p意义下之逆元。

*PS: 没开long long被卡掉45分,果然出题人的话都不可信QAQ。。(学傻了)

题意:将一个数x拆成a1+a2+a3+……,ai不对等aj,求最好深之a1*a2*a3*……。

洛谷 P3811 【模板】乘法逆元


分析:

题目叙述

给定n,p求1~n中持有整数在模p意义下的乘法逆元。

与zqsz的互测题 T1 原题 POJ 1845

1、预处理前缀和前边缀积,因为拆成1针对性乘积没有贡献,所以于2始拆起。

输入输出格式

输入格式:

一行n,p

出口格式:

n行,第i推行代表i在模p意义下之逆元。

输入输出样例

输入样例#1: 

10 13

出口样例#1: 

1
7
9
10
8
11
2
5
3
4

说明

1≤n≤3×106,n<p<20000528

输入保证p为质数。


2、找到一个id,使得2+3+4+……+id

Solution:

首先我们来打听一下逆元。

若a*x≡1(mod
b),a、b互质,则称x为a的逆元,记为a-1。根据逆元的定义,可转化为a*x+b*y=1,用扩展欧几里德法求解。逆元可以据此来以算(t/a)mod
b时,转化为t*a-1mod
b。

运高效幂及扩大欧几里德算法求逆元代码如下:

 

 1 void exgcd(int a,int b,int &x,int &y){
 2     if(a==0){x=0;y=1;return;}
 3     else {
 4         int tx,ty;
 5         exgcd(b%a,a,tx,ty);
 6         x=ty-(b/a)*tx;
 7         y=tx;
 8         return;
 9     }
10 }

 

求逆元还有一个线性算法,具体过程如下。

首先,1-1≡1(mod
p)。

然后,我们设p=k*i+r,r<i,1<i<p,再以这姿势放到mod
p意义下虽会获得:k*i+r≡0(mod
p)

重简单边又随着上i-1、r-1就算会见赢得:

k*r-1+i-1≡0(mod
p)     –>   i-1≡-k*r-1(mod p)    –>   
i-1≡-[p/i]*(p mod i)-1 (mod
p)

于是乎,就好于前面推出时的逆元。代码也就一行:a[i]=-(p/i)*a[p%i];

然后马上道模板题,便是故上述办法解决的。。

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define il inline
 5 #define inf 233333333333
 6 ll n,p,a[3000600],b;
 7 int main()
 8 {
 9     scanf("%lld%lld",&n,&p);
10     a[1]=1;
11     for(int i=2;i<=n;i++){
12     a[i]=-(p/i)*a[p%i];
13     while(a[i]<0)a[i]+=p;
14     printf("%lld\n",a[i-1]);
15     }
16     printf("%lld\n",a[n]);
17     return 0;
18 }
【题目描述】

谜团,是夜魇军团一名强大的战士。他来自远古,是一种不可思议的重力生命体,是来自原始黑暗的扭曲声音,在宇宙中的第一丝光线诞生前就存在的深渊化身。他能动用深渊之力将生物的身体污染,使之转化为自身的碎片“虚灵”。

“虚灵”为谜团所控,拥有一定的战斗力。更为恐怖的是,新产生的“虚灵”的 第一次攻击将吸收被攻击者内心的黑暗,从而分裂成若干个“虚灵”,之后的攻击将无法产生“虚灵”,新产生的“虚灵”第一次攻击仍可产生“虚灵”。 作为天辉军团智囊的你,需要知道某一时刻谜团最多可以产生多少“虚灵”,从而据

此来进行决策。具体的,谜团在 第一秒会转化一个单位,使之变成一个“虚灵”,由于能量消耗过大,谜团在之后的时间内将不再转化单位,使之变成“虚灵”。新产生的“虚灵”会在**下一秒**攻击一次,从而分裂出 m 个“虚灵”,原有的“虚灵”仍然存在,并且将不再产生虚灵。但新产生的“虚灵”可在下一秒攻击一次并分裂,之后也将不再产生“虚灵”(具体见样例解释)。给定时间 t 和分裂数 m,请你告诉天辉军团的战士们,在 t 秒后,谜团拥有多少个“虚灵”。

答案可能很大,要求你模一个数 k(不保证 k 是质数)。


【输入格式】

第一行:三个整数 m,t,k,意义见题目描述

【输出格式】

第一行:t 秒后谜团拥有的能量体个数

【样例输入 1】

3 3 1000

【样例输出 1】

13

【样例输入 2】

3 5 1000

【样例输出 2】

121

【样例 1 解释】

m = 3, t = 5 时:

第一秒:谜团制造 1 个“虚灵”

第二秒:1 个“虚灵”分裂出 3 个“虚灵”,此时共有 4 个“虚灵”

第三秒:第 1 秒制造的 1 个“虚灵”将不能分裂,第 2 秒制造的 3 个“虚灵”,

每个“虚灵”分裂成 3 个“虚灵”,新分裂出的“虚灵”有 9 个,此时共有 13

个“虚灵”。

最终,总共有 13 个“虚灵”

【数据规模与约定】

保证 k,m,t 及答案在 int 范围内(注意只有答案!!!)



原题意是求A^B的约数个数,因为出题人很良心(原话),所以就改为求首项为1,公比为m的等比数列的前n项和。

 部分分
  • 1(sum[id-1]) < x < 2+3+4+……+id(sum[id)。(二分叉查找即可)

 洛谷 P3807【模板】卢卡斯定理

题材叙述

给定n,m,p(1≤n,m,p≤1051\le n,m,p\le
10^51≤n,m,p≤105)

求 Cn+mm mod pC_{n+m}^{m}\ mod\
pCn+mm​ mod p

保证P为prime

C表示组合数。

一个测试点内含有多组数据。

输入输出格式

输入格式:

第一履一个整数T(T≤10T\le 10T≤10),表示数据组数

其次实行开始联名T行,每行三单数n
m p,意义如齐

输出格式:

一同T行,每行一个整数表示答案。

输入输出样例

输入样例#1:
复制

2
1 2 5
2 1 5

出口样例#1:
复制

3 3


则rest = x – sum[id –
1]。将rest分配给2+3+4+id-1中之某部数。

Solution:

Lucas定理。

就是C(n,m) mod
p=C(n%p,m%p)*C(n/p,m/p)%p。

证:不见面。记着即推行。(×)

代码实现者,注意少点:

1.对于​C(n/p,m/p)部分好继续使用Lucas定理递归求解。

2.求逆元,可以据此费马小定理做快速幂,当然为堪线性预处理阶乘逆元。注意,若线性预处理,需要以000号赋位111(很好明,不开说明)。

1~8 暴力枚举 

3、在保管数字不还的前提下,分配受的雅数更是聊更好,证明见4。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define il inline
 5 ll n,m,p,a[100005],b[100005];
 6 il ll gi()
 7 {
 8     ll a=0;char x=getchar();bool f=0;
 9     while((x<'0'||x>'9')&&x!='-')x=getchar();
10     if(x=='-')x=getchar(),f=1;
11     while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
12     return f?-a:a;
13 }
14 il ll fast(ll n,ll m,ll p)
15 {
16     if(m>n)return 0;
17     return a[n]*b[n-m]%p*b[m]%p;
18 }
19 il ll lucas(ll n, ll m ,ll p)
20 {
21     if(!m)return 1;
22     return fast(n%p,m%p,p)*lucas(n/p,m/p,p)%p;
23 }
24 int main()
25 {
26     int t=gi();
27     while(t--)
28     {
29         a[0]=b[0]=a[1]=b[1]=1;
30         n=gi(),m=gi(),p=gi();
31         n+=m;
32         for(int i=2;i<=p;i++)b[i]=-(p/i)*b[p%i]%p;
33         for(int i=2;i<=p;i++)a[i]=a[i-1]*i%p,b[i]=b[i-1]*b[i]%p;
34         printf("%lld\n",(lucas(n,m,p)+p)%p);
35     }
36     return 0;
37 }

据此,应该分配受的勤也id-rest。但是于id-rest小于2常常,那还应该分配受2,2凡是好分配的数字被极度小之数字。

 洛谷 P1082 同余方程

9~12 特殊性质:模数k是质数,因为等比数列的前n项和为 q^n-1/(q-1),除法的取模需要用到逆元,可以用费马小定理做。

a^(p-1) ≡ 1(mod p) ---> a^(p-2) ≡ 1/a (mod p)

1 ll ans=(multiply(T,M)-1)%K*multiply(K-2,M-1)%K%K;
2 printf("%lld",ans);

若分配受4,那么最终之结果吃,mul[id-1]许除去为4(把4夺丢),然后再乘上(4+rest)。

题材叙述

央关于 x
的与余方程 ax ≡ 1 (mod b)的最好小正整数解。


4、证明:设tmp=2*3*4*……*(id

输入输出格式

输入格式:

输入才发一行,包含两单刚刚整数
a, b,用一个空格隔开。

输出格式:

出口只发一行,包含一个正整数
x0,即无限小正整数解。输入数据保证一定有解。

13~16(原话)特殊性质 k不是质数,但k与m互质,用扩展欧几里得求逆元。

但是,我用的扩展欧几里得是要求m-1在%k意义下的逆元啊喂!! 没有给部分分QAQ。

直接打exgcd可以的50分,前提是你开了long long ,但是我并没有开qwqqqq(让我自己一个人静一会)。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 
 8 long long M,T,K,ans;
 9 
10 long long multiply(long long n,long long a)
11 {
12     long long sum=1,x=a;
13     while(n)
14     {
15         if(n&1) sum=sum%K*x%K%K;
16         x=x%K*x%K%K;
17         n>>=1;
18     }
19     return sum%K;
20 }
21 long long exgcd(long long a,long long b,long long &x,long long &y)
22 {
23     if(b==0)
24     {
25         x=1,y=0;
26         return a;
27     }
28     long long d=exgcd(b,a%b,y,x);
29     y=y-a/b*x;
30     return d;
31 }
32 int main()
33 {
34     freopen("mituan.in","r",stdin);
35     freopen("mituan.out","w",stdout);
36     scanf("%lld%lld%lld",&M,&T,&K);
37     {
38         long long x,y;
39         long long d=exgcd(M-1,K,x,y);
40         x%=K;
41         while(x<0) x+=K/d;
42         ans=(multiply(T,M)-1)%K*x%K%K;
43         if(ans<0) ans+=K;
44         printf("%lld",ans);
45     }
46     return 0;
47 }
  • 1)。

输入输出样例

输入样例#1: 

3 10

出口样例#1: 

7

17~20 没有异常性能

假定既可是分配给4,也不过分配给5底状态下,

说明

【数据范围】

对于
40%的数据,2 ≤b≤ 1,000;

对于
60%的数据,2 ≤b≤ 50,000,000;

对于
100%的数据,2 ≤a, b≤ 2,000,000,000。

NOIP 2012
提高组 第二天 第一题

 

将rest分配给4:设tmp=4*t1,则ans1=(4+rest)*t1,

Solution:

求解线性同余方程,直接可以学上扩大欧几里德的模板。a*x≡c
mod(b) 等同于 a*x+b*y=c ,有整数解的必要条件是:c%GCD(a,b)=0
。那么就道题就是以求a*x+b*y=1中x底太小解(特别之当a,b互质时,x为a的逆元)。扩欧我就是无赘述了。


将rest分配给5:设tmp=5*t2,则ans2=(5+rest)*t2,

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define il inline 
 5 il void exgcd(int a,int b,int &x,int &y)
 6 {    
 7     if(!b){x=1,y=0;return;}
 8     exgcd(b,a%b,x,y);
 9     int t=x-a/b*y;
10     x=y;y=t;
11     return;
12 }
13 int a,b,x,y;
14 int main()
15 {
16     scanf("%d%d",&a,&b);
17     exgcd(a,b,x,y);
18     while(x<0)x+=b;
19     cout<<x;
20     return 0;
21 }

 

正要解 1 公式求逆元

安利一个之前看到底博客(为什么自己这尚无优秀看qwq)

http://blog.csdn.net/acdreamers/article/details/8220787

坐费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都见面要求a与m互素。

但是咱来一个公式,若
b|a , 则ans即为(a/b)mod m 的答案:

 

图片 1

证明:

(a/b)mod m = ans

a/b = ans + k * m

a = ans * b + k * m
* b ;

a mod ( bm ) = ans * b

a mod ( bm ) / b = ans

得证。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 #define ull unsigned long long 
 8 
 9 ull M,T,K;
10 
11 ull multiply(ull n,ull a)
12 {
13     ull sum=1,x=a;
14     while(n)
15     {
16         if(n&1) sum=sum%K*x%K%K;
17         x=x%K*x%K%K;
18         n>>=1;
19     }
20     return sum%K;
21 }
22 int main()
23 {
24     freopen("mituan.in","r",stdin);
25     freopen("mituan.out","w",stdout);
26     scanf("%lld%lld%lld",&M,&T,&K);
27     K=K*(M-1);
28     ull ans=(multiply(T,M)-1)/(M-1);
29     printf("%lld",ans);
30     return 0;
31 }

 


 

因为t1>t2,所以ans1>ans2,得证。

正解 2 二分+递归

数学课上的求等比数列的前n项和有一个公式,emmm,公式有很多的变形,其中有一个变形为

图片 2

若另 n 和 m 都等于 t/2 ,可知 


所以我们要求St,只需要每次二分的递归下去,在递归的过程中取模就可以了。

另外注意递归的时候要分t为奇数和偶数的情况,因为计算机里的/是会自动下取整的。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 #define ll long long
 7 
 8 ll M,T,K;
 9 
10 ll pow(ll a,ll n)
11 {
12     ll sum=1,x=a;
13     while(n)
14     {
15         if(n&1) sum=sum%K*x%K%K;
16         x=x%K*x%K%K;
17         n>>=1;
18     }
19     return sum;
20 }
21 
22 ll sum(ll q,ll t) // q^0+q^1+q^2+...+q^t --> S(t+1)
23 {
24     if(t==0) return 1;
25     if(t%2==0) return (sum(q,t/2-1)%K*(1+pow(q,t/2))%K%K+pow(q,t)%K)%K; 
26     //t 为偶数,说明S(t+1)没法直接通过S((t+1)/2)求得,就去求St,最后加上一个q^t次方 
27     else return (sum(q,t/2)%K*(1+pow(q,t/2+1))%K)%K; 
28     //t 为奇数,说明S(t+1)可以直接用(S(t+1)/2)求得,又S((t+1)/2)即为sum(q,t/2)(t/2下取整),可以直接求 (t/2+1即为(t+1)/2)  
29 }
30 int main()
31 {
32     freopen("mituan.in","r",stdin);
33     freopen("mituan.out","w",stdout);
34     scanf("%d%d%d",&M,&T,&K);
35     printf("%lld",sum(M,T-1));
36     return 0;
37 }

 

 

 

 

5、PS:2吗偶数,3吗奇数,2x+3y可以表示过1之持有正整数,3极端多的下最好理想。

6、逆元

1、除法取模要就此逆元。

赶上(a/b)%mod这种情况,应转化成(a*k)%mod,k即为a在mod下之逆元。

2、递推求逆元

inv[1]=1
for(i=2;i<n;i++)
inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD

3、定义:

若a*b=1(mod
MOD)则称作b是a在模MOD下之逆元。记作a^-1=b(mod m)

4、性质: a^-1=b(mod
MOD)时

b^-1=a(mod MOD)

a*b=1(mod
MOD),即a*b=MOD*x+1.

a^-1=b+k*MOD(mod
MOD)

MOD/a=MOD*b(mod
MOD)

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100000 + 10;
typedef long long LL;
LL sum[MAXN], mul[MAXN], inv[MAXN];
const LL MOD = 1e9 + 7;
void init(){
    sum[1] = 0, mul[1] = 1, inv[1] = 1;
    for(int i = 2; i < MAXN; ++i){
        sum[i] = sum[i - 1] + i;
        mul[i] = ((mul[i - 1] % MOD) * (i % MOD)) % MOD;
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    }
}
int main(){
    int T;
    scanf("%d", &T);
    init();
    while(T--){
        LL x;
        scanf("%lld", &x);
        if(x == 1){
            printf("1\n");
            continue;
        }
        int id = lower_bound(sum + 2, sum + MAXN, x) - sum;
        if(sum[id] == x){
            printf("%lld\n", mul[id]);
            continue;
        }
        --id;
        LL rest = x - sum[id];
        LL ans;
        if(2 + rest > id){
            ans = ((mul[id] * inv[2]) % MOD * (2 + rest)) % MOD;
        }
        else{
            ans = ((mul[id] * inv[id + 1 - rest]) % MOD * (id + 1)) % MOD;
        }
        printf("%lld\n", ans % MOD);
    }
    return 0;
}

  

相关文章