题目链接:
HDU 4373 Mysterious For
题意:
定义两种循环:
for(int a[i]=0;a[i]<n;++a[i])...
for(int a[i]=a[i−1];a[i]<n;++a[i])...
会有
num
层循环属于第一种循环,给出这些循环的位置,求总共的循环次数是多少?答案
mod 364875103
分析:
首先我们考虑第一种循环下面跟
k−1
个第二种循环,总共是
k
层循环的情况。定义在
- 对于第一层循环,循环到
i
,显然有
f1(i)=1总的循环次数是:∑f1(i)=n=C1n - 对于第二层循环,因为第二层循环属于第二种循环,想要循环到
i
,那么相应的第一层循环就要
≤i ,也就是f2(i)=∑j≤if1(j)=i总的循环次数是:∑f2(i)=(n+1)∗n2=C2n+1 - 对于第三层循环,同理可得:
f3(i)=∑j≤if2(i)=(i+1)∗i2总的循环次数是:∑f3(i)=∑12∗(i2+i)=(n+2)∗(n+1)∗(n)6=C3n+2
- …
- 可以发现对于第
k
层循环总的循环次数是
Ckn+k−1
接着我们需要计算
Ckn+k−1 %364875103的值
.比较坑爹的是
364875103
不是一个素数!但是对
364875103
进行素数检查时可以发现
364875103=97∗3761599,
而这两个因子都是素数,令
mod1=97,mod2=3761599,mod=364875103
令
m=k,n=n+k−1
,我们
则有:
因为 mod1,mod2 都是素数,所以 x1和x2 都可以用 Lucas定理求解出来。 那么剩下的问题就是求解上面的 同余方程了。
利用 中国剩余定理求解同余方程。
我们定义:
那么答案就是:
最后在根据乘法原理易知每段求得的答案需要累乘。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int mod1 = 97;
const int mod2 = 3761599;
const ll MOD = 364875103;
const int MAX_M = 100010;
ll fac1[mod1 + 10], fac2[mod2 + 10], e1, e2;
ll quick_pow(ll a, ll b, ll mod)
{
ll res = 1, tmp = a;
while(b) {
if(b & 1) res = res * tmp % mod;
tmp = tmp * tmp % mod;
b >>= 1;
}
return res;
}
void init()
{
fac1[0] = fac2[0] = 1;
for(int i = 1; i < mod1; ++i) { fac1[i] = fac1[i - 1] * i % mod1; }
for(int i = 1; i < mod2; ++i) { fac2[i] = fac2[i - 1] * i % mod2; }
e1 = quick_pow(mod2, mod1 - 2, mod1) * mod2;
e2 = quick_pow(mod1, mod2 - 2, mod2) * mod1;
}
ll ex_gcd(ll a, ll b, ll& x, ll& y)
{
if(b == 0) {
x = 1, y = 0;
return a;
}
ll r = ex_gcd(b, a % b, y, x);
y -= a / b * x;
return r;
}
ll inv(ll a, ll n)
{
ll x, y;
ll d = ex_gcd(a, n, x, y);
if(d == 1) return (x % n + n) % n;
else return 0;
}
ll C(int n, int m, int id, int mod)
{
if(m > n) return 0;
ll a, b, c;
if(id == 1) {
a = fac1[n], b = fac1[n - m], c = fac1[m];
} else {
a = fac2[n], b = fac2[n - m], c = fac2[m];
}
//return a * inv(b * c % mod, mod) % mod; //也可以用扩展欧几里德求逆元
return a * quick_pow(b * c % mod, mod - 2, mod) % mod;
}
ll Lucas(int n, int m, ll mod, int id)
{
if(m == 0) return 1;
return C(n % mod, m % mod, id, mod) * Lucas(n / mod, m / mod, mod, id) % mod;
}
int main()
{
init();
int T, n, m, num, data[30], cases = 0;
scanf("%d", &T);
while(T--) {
scanf("%d%d%d", &n, &m, &num);
for(int i = 0; i < num; ++i) { scanf ("%d", &data[i]); }
data[num++] = m;
ll ans = 1;
for(int i = 1; i < num; ++i) {
int k = data[i] - data[i - 1];
ll t1 = Lucas(n + k - 1, k, mod1, 1) * e1 % MOD;
ll t2 = Lucas(n + k - 1, k, mod2, 2) * e2 % MOD;
ans = ans * (t1 + t2) % MOD;
}
printf("Case #%d: %lld\n", ++cases, ans);
}
return 0;
}