数学知识
矩阵乘法
斐波那契前 n 项和
#include <bits/stdc++.h>
using namespace std;
int c, mod;
struct matrix
{
int n, m, a[4][4];
matrix operator * (const matrix &x) const
{
matrix y;
y.n = n, y.m = x.m;
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= x.m; ++ j)
{
y.a[i][j] = 0;
for (int k = 1; k <= m; ++ k)
{
y.a[i][j] = (y.a[i][j] + (long long)a[i][k] * x.a[k][j] % mod) % mod;
}
}
}
return y;
}
};
matrix quick(matrix x, int b)
{
matrix res;
res.n = x.m, res.m = x.m;
for (int i = 1; i <= res.n; ++ i)
{
for (int j = 1; j <= res.m; ++ j)
{
if(i == j) res.a[i][j] = 1;
else res.a[i][j] = 0;
}
}
while(b)
{
if(b & 1) res = res * x;
x = x * x;
b >>= 1;
}
return res;
}
int main()
{
scanf("%d %d", &c, &mod);
matrix x, y;
x.n = 1, x.m = 3, y.n = 3, y.m = 3;
x.a[1][1] = 1, x.a[1][2] = 1, x.a[1][3] = 1;
y.a[1][1] = 0, y.a[1][2] = 1, y.a[1][3] = 0;
y.a[2][1] = 1, y.a[2][2] = 1, y.a[2][3] = 1;
y.a[3][1] = 0, y.a[3][2] = 0, y.a[3][3] = 1;
x = x * quick(y, c - 1);
printf("%d", x.a[1][3]);
return 0;
}
佳佳的斐波那契
#include <bits/stdc++.h>
using namespace std;
int c, mod;
struct matrix
{
int n, m, a[6][6];
matrix operator * (const matrix &x) const
{
matrix y;
y.n = n, y.m = x.m;
for (int i = 0; i < n; ++ i)
{
for (int j = 0; j < x.m; ++ j)
{
y.a[i][j] = 0;
for (int k = 0; k < m; ++ k)
{
y.a[i][j] = (y.a[i][j] + (long long)a[i][k] * x.a[k][j] % mod) % mod;
}
}
}
return y;
}
};
matrix quick(matrix x, int b)
{
matrix res;
res.n = x.m, res.m = x.m;
for (int i = 0; i < res.n; ++ i)
{
for (int j = 0; j < res.m; ++ j)
{
if(i == j) res.a[i][j] = 1;
else res.a[i][j] = 0;
}
}
while(b)
{
if(b & 1) res = res * x;
x = x * x;
b >>= 1;
}
return res;
}
int main()
{
scanf("%d %d", &c, &mod);
matrix x, y;
x.n = 1, x.m = 5, y.n = 5, y.m = 5;
x.a[0][0] = 1, x.a[0][1] = 1, x.a[0][2] = 1, x.a[0][3] = 2, x.a[0][4] = 1;
y.a[0][0] = 0, y.a[0][1] = 1, y.a[0][2] = 0, y.a[0][3] = 2, y.a[0][4] = 0;
y.a[1][0] = 1, y.a[1][1] = 1, y.a[1][2] = 0, y.a[1][3] = 1, y.a[1][4] = 0;
y.a[2][0] = 0, y.a[2][1] = 0, y.a[2][2] = 0, y.a[2][3] = 1, y.a[2][4] = 0;
y.a[3][0] = 0, y.a[3][1] = 0, y.a[3][2] = 1, y.a[3][3] = 1, y.a[3][4] = 1;
y.a[4][0] = 0, y.a[4][1] = 0, y.a[4][2] = 0, y.a[4][3] = 0, y.a[4][4] = 1;
x = x * quick(y, c - 1);
printf("%d", x.a[0][4]);
return 0;
}
GT考试
我真的无语了,这个题目简化版是做过的,完全没有印象了捏
状态表示\(f[i][j]\):
集合:所有长度为i,且不包含S串,且末尾部分与S的前缀匹配最大长度是j的所有字符串的集合。
属性:集合里面元素的总和。
状态计算(集合划分):看\(f[i][j]\)可以构造出来哪些\(f[i +1][k]\)。枚举最后一位是什么数字,然后用kmp去计算\(j\)变成了什么,这就是新的状态。
一般的\(dp[i][j]\)都可以变成\(F[i] = A * F[i - 1]\)的形式,其中A是一个矩阵。如果A是固定的,并且j不是很大(矩阵的时间复杂度是三次方)的话,那就可以用快速幂来算了。
#include <bits/stdc++.h>
using namespace std;
const int N = 22;
char s[N];
int nxt[N], c, d, mod;
void kmp()
{
for (int i = 0, j = 2; j <= d; ++ j)
{
while(i && s[i + 1] != s[j]) i = nxt[i];
if(s[i + 1] == s[j]) i ++;
nxt[j] = i;
}
return ;
}
struct matrix
{
int a[N][N], n, m;
matrix operator * (const matrix &x) const
{
matrix y;
y.n = n, y.m = x.m;
for (int i = 0; i < n; ++ i)
{
for (int j = 0; j < x.m; ++ j)
{
y.a[i][j] = 0;
for (int k = 0; k < m; ++ k) y.a[i][j] = (y.a[i][j] + a[i][k] * x.a[k][j] % mod) % mod;
}
}
return y;
}
};
matrix quick(matrix x, int b)
{
matrix res;
res.n = x.m, res.m = x.m;
for (int i = 0; i < res.n; ++ i)
{
for (int j = 0; j < res.m; ++ j)
{
if(i == j) res.a[i][j] = 1;
else res.a[i][j] = 0;
}
}
while(b)
{
if(b & 1) res = res * x;
x = x * x;
b >>= 1;
}
return res;
}
int main()
{
scanf("%d %d %d", &c, &d, &mod);
scanf("%s", s + 1);
kmp();
matrix x, y;
x.n = 1, x.m = d, y.n = d, y.m = d;
for (int i = 0; i < d; ++ i)
{
x.a[0][i] = 0;
for (int j = 0; j < d; ++ j) y.a[i][j] = 0;
}
x.a[0][0] = 9, x.a[0][1] = 1;
for (int i = 1; i <= d; ++ i) s[i] -= '0';
for (int st = 0; st <= d - 1; ++ st)
{
for (int x = 0; x <= 9; ++ x)
{
int p = st;
while(p && s[p + 1] != x) p = nxt[p];
if(s[p + 1] == x) p ++;
if(p < d) y.a[st][p] ++;
}
}
x = x * quick(y, c - 1);
int sum = 0;
for (int i = 0; i < d; ++ i) sum = (sum + x.a[0][i]) % mod;
printf("%d", sum);
return 0;
}
筛质数
欧拉筛:每个合数被它最小的质因数筛去,所以时间复杂度是O(n)的。
1不是质数
如果当前数字是质数,把这个数字加到prime
暴力枚举以前的所有质数,如果当前数字q % now(质数) == 0说明q里面有一个质数会比以后的质数都要小,那么就不能继续计算了,否则q * now就是合数,打上标记就可以了。
抽象
哥德巴赫猜想
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
bool vis[N];
vector<int> pri;
int n;
void prime()
{
vis[1] = 1;
for (int i = 2; i <= N - 5; ++ i)
{
if(!vis[i]) pri.push_back(i);
for (int j = 0; j < pri.size(); ++ j)
{
if((long long)i * pri[j] > N - 5) break;
vis[i * pri[j]] = 1;
if(i % pri[j] == 0) break;
}
}
return ;
}
int main()
{
prime();
while(scanf("%d", &n) != EOF)
{
if(n == 0) break;
bool pd = false;
for (int i = 1; i < pri.size(); ++ i)
{
if(pri[i] > n / 2) break;
if(!vis[n - pri[i]])
{
printf("%d = %d + %d\n", n, pri[i], n - pri[i]);
pd = true;
break;
}
}
if(!pd) printf("Goldbach's conjecture is wrong.\n");
}
return 0;
}
夏洛克和他的女朋友
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
bool vis[N];
vector<int> pri;
int n;
void prime()
{
vis[1] = 1;
for (int i = 2; i <= n + 1; ++ i)
{
if(!vis[i]) pri.push_back(i);
for (int j = 0; j < pri.size(); ++ j)
{
if((long long)i * pri[j] > n + 1) break;
vis[i * pri[j]] = 1;
if(i % pri[j] == 0) break;
}
}
return ;
}
int main()
{
scanf("%d", &n);
prime();
if(n <= 2)
{
printf("1\n");
for (int i = 1; i <= n; ++ i) printf("1 ");
}
else
{
printf("2\n");
for (int i = 2; i <= n + 1; ++ i)
{
if(!vis[i]) printf("1 ");
else printf("2 ");
}
}
return 0;
}
约数个数
(* ̄(oo) ̄):1可以作为约数。
约数:若一个数x可以整除y,那么x是y的一个约数。
若\(N = P_{1} ^ {c_{1}} + P_{2} ^ {c_{2}} + ... + P_{k} ^ {c_{k}}\)则\(N\)的约数个数
\(F(N) = (c_{1} + 1) \times (c_{2} + 1) \times ... \times (c_{k} + 1)\)
\(\sum_{i = 1}^{N} F[i] = \sum_{i = 1}^{N} \frac{N}{i}\)所以总和是一个调和级数,大约为\(O(nlog_{n})\)
关于常数的结论:
int 范围内约数个数最多的数 的 约数个数为1600
轻拍牛头
可以暴力枚举每一个数字,求它的约数是多少,然后直接累加,时间复杂度是\(O(n \sqrt{n})\)。
把一个数字分解质因数其实也是非常麻烦的。
我们可以反过来求,每个数字有多少个数是它的倍数。
从1到1000000循环,判断有多少个数字是i的倍数,这个判断不是简单的除法,而是精准到那个倍数是多少,也就是说第二层循环不断跳i。
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n, ans[N], cnt[N], a[N], len;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++ i)
{
scanf("%d", &a[i]);
cnt[a[i]] ++, len = max(len, a[i]);
}
for (int i = 1; i <= len; ++ i)
{
for (int j = i; j <= len; j += i) ans[j] += cnt[i];
}
for (int i = 1; i <= n; ++ i) printf("%d\n", ans[a[i]] - 1);
return 0;
}
樱花
拿到题目的第一步可以把公式推一下,看看有没有什么性质。
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n, vis[N], cnt[N], ans = 1;
const int mod = 1000000007;
vector<int> pri;
int main()
{
scanf("%d", &n);
vis[1] = 1;
for (int i = 2; i <= n; ++ i)
{
if(!vis[i])
{
pri.push_back(i);
for (int j = i; j <= n; j += i)
{
int x = j;
if(j != i) vis[j] = 1;
while(x && x % i == 0) cnt[i] += 1, x /= i;
}
}
}
for (int i = 0; i < pri.size(); ++ i) ans = (long long)ans * (2 * cnt[pri[i]] + 1) % mod;
printf("%d", ans);
return 0;
}
反素数
要找的节点就是[1, N]中约数个数最多的最小的数字。
N <= 2e9让我们发现:
1.最多只会有9个质因子2
2.同一个质因子的数量小于等于30
3.质因子从小到大排列时,每个质因子的数量一定是不上升的。
那么我们就可以开始愉快的暴力搜索了。
#include <bits/stdc++.h>
using namespace std;
const int inf = 2000000000;
typedef long long ll;
int g = 0, ans = inf;
int n, a[15] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23};
int b[25];
ll quick(ll a, int b)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
void dfs(int pos, int pre, int sum, int num)
{
if(pos == 10)
{
if(sum > g) g = sum, ans = num;
else if(sum == g) ans = min(ans, num);
return ;
}
for (int i = 0; i <= pre; ++ i)
{
ll x = (ll)num * quick(a[pos], i);
if(x > n) break;
b[pos] = i;
dfs(pos + 1, i, sum * (i + 1), x);
b[pos] = 0;
}
return ;
}
int main()
{
scanf("%d", &n);
dfs(1, inf, 1, 1);
printf("%d", ans);
return 0;
}
Hankson的趣味题
真的,想法暴力一点,就是一个暴力枚举
#include <bits/stdc++.h>
using namespace std;
const int N = 45000;//44800
bool vis[N];
int sum, num[15], cnt[15], tot = 0;
vector<int> pri;
void prime()
{
vis[1] = 1;
for (int i = 2; i <= 44730; ++ i)
{
if(!vis[i]) pri.push_back(i);
for (int j = 0; j < pri.size() && (long long)pri[j] * i <= 44730; ++ j)
{
vis[pri[j] * i] = 1;
if(i % pri[j] == 0) break;
}
}
return ;
}
int a0, a1, b0, b1;
int ans = 0, f[15][34];
void dfs(int pos, int x)
{
if(pos == tot + 1)
{
if(__gcd(x, a0) == a1 && (long long)x * b0 / __gcd(x, b0) == b1) ans ++;
return ;
}
for (int i = 0; i <= cnt[pos]; ++ i)
{
dfs(pos + 1, x * f[pos][i]);
}
return ;
}
int main()
{
prime();
int T;
scanf("%d", &T);
while(T --)
{
scanf("%d %d %d %d", &a0, &a1, &b0, &b1);
tot = 0;
int x = b1;
for (int i = 0; i < pri.size() && (long long)pri[i] * pri[i] <= x; ++ i)
{
if(x % pri[i] == 0)
{
num[++ tot] = pri[i], cnt[tot] = 0;
while(x && x % num[tot] == 0) x = x / num[tot], cnt[tot] ++;
}
}
if(x != 1) num[++ tot] = x, cnt[tot] = 1;
for (int i = 1; i <= tot; ++ i)
{
f[i][0] = 1;
for (int j = 1; j <= cnt[i]; ++ j) f[i][j] = f[i][j - 1] * num[i];
}
ans = 0;
dfs(1, 1);
printf("%d\n", ans);
}
return 0;
}
欧拉函数
$\varphi(n) = \(比\)n$小的和n互质的数
若n只有一个质因数\(\varphi(n) = P ^k - P^ {k - 1}\)
若a、b互质$\varphi(ab) = \varphi(a) \times \varphi(b) $
\(\varphi(n) = n \times (1 - \frac{1}{P_{1}}) \times ... \times (1 - \frac{1}{P_{k}})\)
可见的点
#include <bits/stdc++.h>
using namespace std;
const int N = 3005;
int n, ph[N], T, sum[N];
bool vis[N];
vector<int> pri;
void get_phi()
{
vis[1] = 1, ph[1] = 1;
for (int i = 2; i <= 3000; ++ i)
{
if(!vis[i])
{
pri.push_back(i);
ph[i] = i - 1;
}
for (int j = 0; j < pri.size() && pri[j] * i <= 3000; ++ j)
{
vis[i * pri[j]] = 1, ph[i * pri[j]] = ph[i] * ph[pri[j]];
if(i % pri[j] == 0)
{
ph[i * pri[j]] = ph[i] * pri[j];
break;
}
ph[i * pri[j]] = ph[i] * (pri[j] - 1);
}
}
return ;
}
int main()
{
get_phi();
for (int i = 1; i <= 3000; ++ i) sum[i] = sum[i - 1] + ph[i];
scanf("%d", &T);
for (int t = 1; t <= T; ++ t)
{
scanf("%d", &n);
printf("%d %d %d\n", t, n, 2 + sum[n] * 2 - 1);
}
return 0;
}
最大公约数
#include <bits/stdc++.h>
using namespace std;
const int N = 10000005;
int n, ph[N], sum[N];
typedef long long ll;
ll ans = 0;
bool vis[N];
vector<int> pri;
int main()
{
scanf("%d", &n);
vis[1] = 1, ph[1] = 1;
for (int i = 2; i <= n; ++ i)
{
if(!vis[i])
{
ph[i] = i - 1;
sum[i] = 1;
pri.push_back(i);
}
for (int j = 0; j < pri.size() && (ll)i * pri[j] <= n; ++ j)
{
vis[i * pri[j]] = 1;
if(i % pri[j] == 0)
{
ph[i * pri[j]] = ph[i] * pri[j];
break;
}
ph[i * pri[j]] = ph[i] * (pri[j] - 1);
}
sum[i] += sum[i - 1];
}
for (int i = 2; i <= n; ++ i)
{
ans = ans + ph[i] * sum[n / i];
}
printf("%lld", ans * 2ll + sum[n]);
return 0;
}
同余
同余方程
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a, b;
void exgcd(ll a, ll b, ll &x, ll &y)
{
if(!b)
{
x = 1, y = 0;
return ;
}
exgcd(b, a % b, x, y);
ll t = x;
x = y, y = t - y * (a / b);
return ;
}
int main()
{
scanf("%lld %lld", &a, &b);
ll x, y;
exgcd(a, b, x, y);
printf("%lld", (x % b + b) % b);
}
青蛙的约会
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll L, x, y, m, n;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
ll g = exgcd(b, a % b, x, y);
ll t = x;
x = y, y = t - y * (a / b);
return g;
}
int main()
{
scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &L);
ll t, p;
ll g = exgcd(m - n, L, t, p);
if((y - x) % g != 0) printf("Impossible");
else
{
ll v = L / g;
if(v < 0) v = -v;
t = t * ((y - x) / g);
t = (t % v + v) % v;
printf("%lld", t);
}
}
曹冲养猪
#include <bits/stdc++.h>
using namespace std;
typedef __int128 ll;
ll read()
{
ll x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
void write(ll x)
{
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
return ;
}
void exgcd(ll a, ll b, ll &x, ll &y)
{
if(b == 0)
{
x = 1, y = 0;
return ;
}
exgcd(b, a % b, x, y);
ll t = x;
x = y, y = t - y * (a / b);
return ;
}
ll ans = 0, a[15], b[15], M = 1;
int n;
int main()
{
scanf("%d", &n);
cout << n << endl;
for (int i = 1; i <= n; ++ i)
{
a[i] = read(), b[i] = read(), M = M * a[i];
}
for (int i = 1; i <= n; ++ i)
{
ll Mi = M / a[i];
ll x, y;
exgcd(Mi, a[i], x, y);
ans = (ans + x * Mi % M * b[i] % M) % M;
}
write((ans % M + M) % M);
return 0;
}
组合计数
组合计数是一种方法和一种思想,包含乘法原理、加法原理、组合数、容斥原理、Lucas、Catalan定理等。
牡牛和牝牛(递推法)
\(f[i]\)所有长度是i的,且以1(牧牛)结尾的字符串的数量。
上一个1的位置 <= i - k - 1
注意:\(f[0] = 1\)可以表示前面没有1的一种方案
\(f[i] = \sum_{j = 0}^{i - k - 1}f[j]\)
通过将所有\(f[i]\)累加,暴力枚举最后一个1的位置来计算。
通过记录每次的前缀和来优化。
#include <bits/stdc++.h>
using namespace std;
const int mod = 5000011, N = 100005;
int n, k, dp[N], sum = 0, ans = 0;
int main()
{
scanf("%d %d", &n, &k);
dp[0] = 1, sum = 1;
for (int i = 1; i <= k; ++ i) dp[i] = 1;
for (int i = k + 1; i <= n; ++ i)
{
dp[i] = sum;
sum = (sum + dp[i - k]) % mod;
}
for (int i = 0; i <= n; ++ i) ans = (ans + dp[i]) % mod;
printf("%d", ans);
return 0;
}
方程的解(隔板法)
从组合方向考虑解决问题。
\(x^x mod 1000\)可以使用快速幂来求解
\(a_{1} +a_{2} + a_{3} + ...+a_{n} = x^x mod 1000\)
可以用映射的方法就变成了小球和隔板的问题
任何一个方案都可以对应一组a的解。任何一组a的解都可以对应一个放隔板的方案。所以这两个集合是映射关系。
\(ans = C_{n - 1}^{x ^ x mod 1000 - 1}\)
很不幸运的是答案并没有让我们对于某一个数字取模,所以我们需要手写高精度。jflkdsaj
#include <bits/stdc++.h>
using namespace std;
struct data
{
int a[150];
void mem()
{
for (int i = 1; i < 150; ++ i) a[i] = 0;
return ;
}
data operator + (const data &g) const
{
data c;
int t = 0;
for (int i = 1; i < 150; ++ i)
{
t += a[i] + g.a[i];
c.a[i] = t % 10;
t /= 10;
}
return c;
}
}f[1000][100];
const int mod = 1000;
int qmi(int a, int b)
{
int res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int k, x;
int main()
{
scanf("%d %d", &k, &x);
x = qmi(x % mod, x);
for (int i = 0; i < x; ++ i)
{
for (int j = 0; j <= i && j < k; ++ j)
{
if(!j) f[i][j].mem(), f[i][j].a[1] = 1;
else f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
}
}
int i = 149;
while(!f[x - 1][k - 1].a[i]) i --;
while(i > 0)
{
printf("%d", f[x - 1][k - 1].a[i]);
i --;
}
return 0;
}
车的放置(加法原理)
如果整体做的话可以使用乘法原理,但是这个问题整天做不了。
所以采用分成若干种互不相交的子集的思想,然后相加,对应的就是加法原理。
尝试将这个不规则图形变成两个规则的矩形。
规则矩形[n, m]中放置k个車的方案是C(n, k) * P(m, k)
然后枚举上面和下面分别放置的个数。上半部分放置的位置对下面的影响是固定的。
#include <bits/stdc++.h>
using namespace std;
const int N = 2005, mod = 100003;
int a, b, c, d, fac[N], rev[N], ans = 0, k;
int C(int x, int y)
{
if(x < y) return 0;
return (long long)fac[x] * rev[y] % mod * rev[x - y] % mod;
}
int P(int x, int y)
{
if(x < y) return 0;
return (long long)fac[x] * rev[x - y] % mod;
}
int quick(int a, int b)
{
int res = 1;
while(b)
{
if(b & 1) res = (long long)res * a % mod;
a = (long long)a * a % mod;
b >>= 1;
}
return res;
}
int main()
{
scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
int len = max(max(a + c, b), d);
if(k > len) printf("0");
else
{
fac[0] = 1;
for (int i = 1; i <= len; ++ i) fac[i] = fac[i - 1] * i % mod;
rev[len] = quick(fac[len], mod - 2);
for (int i = len; i >= 1; -- i) rev[i - 1] = rev[i] * i % mod;
for (int i = 0; i <= k; ++ i)
{
ans = (ans + (long long)C(a, i) * P(b, i) % mod * C(d, k - i) % mod * P(a + c - i, k - i) % mod) % mod;
}
printf("%d", ans);
}
return 0;
}
数三角形(容斥原理)
求出总共的方案数,然后把不合法的方案数减去
枚举一个直角三角形,直线一定在直角三角形的斜边上。
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n, m;
typedef long long ll;
int gcd(int a, int b)
{
if(!b) return a;
return gcd(b ,a % b);
}
ll C(int a)
{
return (ll)a * (a - 1) * (a - 2) / 6;
}
int main()
{
scanf("%d %d", &n, &m);
n ++, m ++;
ll res = C(n * m) - C(m) * n - C(n) * m;
n --, m --;
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= m; ++ j) res = res - 2ll * (gcd(i, j) - 1) * (n - i + 1) * (m - j + 1);
}
printf("%lld", res);
return 0;
}
序列统计(隔板法)
对于这种有序列长度,求序列方案数的,考虑一下隔板法。
可以将题目转化成不定方程的非负整数解问题,然后不等式两边同时加上区间的长度,就变成了不定方程的正整数解问题,就可以使用隔板法了。注意,这里的隔板代表的数字是隔板之前一共有多少个元素。当然,我们要枚举这个序列的长度,也就是不定方程中未知数的个数。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p = 1000003;
int r, l, n;
int quick(int a, int b)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll)res * a % p;
a = (ll)a * a % p;
b >>= 1;
}
return res;
}
int C(int a, int b)
{
int up = 1, down = 1;
for (int i = a, j = 1; j <= b; ++ j, -- i)
{
up = (ll)up * i % p;
down = (ll)down * j % p;
}
return (ll)up * quick(down, p - 2) % p;
}
int Lucas(int a, int b)
{
if(a < p && b < p) return C(a, b);
return (ll)Lucas(a / p, b / p) * C(a % p, b % p) % p;
}
int main()
{
int T;
scanf("%d", &T);
while(T --)
{
scanf("%d %d %d", &n, &l, &r);
printf("%d\n", (Lucas(r - l + n + 1, r - l + 1) + p - 1) % p);
}
return 0;
}
容斥原理
Devu和鲜花
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
const int N = 25;
int n, vis[N];
ll a[N], m, ans = 0;
ll quick(ll a, ll b)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll C(ll a, ll b)
{
if(a == b || b == 0) return 1;
if(a < b) return 0;
ll up = 1, down = 1;
for (ll i = a, j = 1; j <= b; j ++, i --)
{
up = up * (i % mod) % mod;
down = down * (j % mod) % mod;
}
return up * quick(down, mod - 2) % mod;
}
void dfs(int x)
{
if(x == n + 1)
{
ll p = m + n;
int tot = 0;
for (int i = 1; i <= n; ++ i)
{
if(vis[i]) p -= (a[i] + 1), tot ++;
}
if(tot & 1) ans = (ans - C(p - 1, n - 1)) % mod;
else if(tot != 0) ans = (ans + C(p - 1, n - 1)) % mod;
return ;
}
vis[x] = 1;
dfs(x + 1);
vis[x] = 0;
dfs(x + 1);
}
int main()
{
scanf("%d %lld", &n, &m);
for (int i = 1; i <= n; ++ i) scanf("%lld", &a[i]);
dfs(1);
ans = (ans + C(m + n - 1, n - 1)) % mod;
printf("%lld", (ans + mod) % mod);
return 0;
}

浙公网安备 33010602011771号