【数论】三个袋子

来源:互联网 发布:万网和阿里云 编辑:程序博客网 时间:2024/05/17 08:47

三个袋子

(bags.pas/c/cpp/in/out)

时限:1 sec | 内存:64 MB

背景

    平平在公园里游玩时捡到了很多小球,而且每个球都不一样。平平找遍了全身只发现了3个一模一样的袋子。他打算把这些小球都装进袋子里(袋子可以为空)。他想知道他总共有多少种放法。

 

题目描述

    将N个不同的球放到3个相同的袋子里,求放球的方案总数M。

    结果可能很大,我们仅要求输出M mod K的结果。

    现在,平平已经统计出了N<=10的所有情况。见下表:

N

1

2

3

4

5

6

7

8

9

10

M

1

2

5

14

41

122

365

1094

3281

9842

 

输入

两个整数N,K,N表示球的个数。

 

输出

输出仅包括一行,一个整数Mmod K 。

 

输入样例 (bags.in)

11 10000

 

输出样例(bags.out)

9525

 

数据规模

对于 40%数据,10<=N<=10,000

对于100%数据,10<=N<=1,000,000,000

对于 100%数据,K<=100,000



我一开始用的最老实的办法,用排列组合,sigma(C(n,i)*C(n-1,j)*C(n-2,n-i-j),if i==j==k then div 6 else if i==j||i==k||j==k then div 2)

(以上口水话有点多。= =、)


实际上可以发现规律f[n] = 3*f[n-1]-1

求出通项公式f[n] = (3^(n-1)+1)/2

然后可以用快速幂。


这里的2不好处理,因为要求模,但是除法没有同余定理,所以我一开始用的乘法逆元,但是,当除数是偶数(也就是和2不互质时),乘法逆元无解。所以WA60

这里有一个重要的结论。a/b (mod c) = (a%(b*c))/b。网上找了两个版本的证明过程,后者更容易理解些:


假设a = x*b*c + y*b + z,其中xyz都是非负数,使得0 <= y*b+z < c0 <= z < b

左边 = (a / b) mod c = ((x*b*c + y*b + z) / b) mod c = (x*c + y) mod c = yy肯定是小于c的,不然由于b>0,c>00 <= y*b + z < c,这一条件不会满足)

右边 = (a mod (b*c)) / b = ((x*b*c + y*b + z) mod (b*c)) / b = (y*b + z) / b = y

左边=右边


(a/b)%k=(a%(b*k))/b   gcd(a,b)=b 证明如下:

设(a/b)%k=m,那么存在一个正整数n使得a/b=n*k+m

那么a=n*b*k+b*m,  a=n*(b*k)+(b*m)所以a%(b*k)=b*m

(a/b)%k=(a%(b*k))/b,证闭。


#include <cstdio>long long quickpower(long a,long b,long c){long long rs = 1;long long tmp = a;while (b){if (b&1){rs=(rs*tmp)%c;}tmp = (tmp*tmp)%c;b >>= 1;}return rs;}int main(){freopen("bags.in","r",stdin);freopen("bags.out","w",stdout);long n,k;scanf("%ld%ld",&n,&k);printf("%ld",(quickpower(3,n-1,k<<1)+1)>>1);return 0;}