数学知识
筛素数
埃氏筛法 ( O ( l o g l o g n ) ) (O(log logn)) (O(loglogn))
//埃式筛法
//求n以内的质数的个数
int p;
int prime[maxn];
bool vis[maxn];
int prime(int n)
{
p = 0;
for(int i = 0; i <= n; i++) vis[i] = true;
vis[0] = vis[1] = false;
for (int i = 2; i <= n; ++i){
if (vis[i]){
prime[p++] = i;
for (int j = 2*i; j <= n; j += i) // 每次筛掉素数的倍数
vis[j] = false;
}
}
return p;
}
线性筛法 O ( n ) O(n) O(n)
// 保证每个数都被其最小质因子筛掉
int primes[MAXN], cnt; // primes[]存储所有素数
bool vis[MAXN]; // vis[x]存储x是否被筛掉
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!vis[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] * i <= n ; j ++ ) // 枚举的是质数的i倍
{
vis[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
1292. 哥德巴赫猜想 - AcWing题库
题意:验证哥德巴赫猜想。任意一个大于 4 的偶数都可以拆成两个奇素数之和。
线性筛后,从小到大枚举每一个质数,二分查找 n − p r i m e s [ i ] n-primes[i] n−primes[i]
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN = 1e6+50;
int n;
bool vis[MAXN];
int primes[MAXN], cnt;
void get_primes(int n)
{
for(int i = 2;i <= n;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;primes[j] * i <= n;j++)
{
vis[primes[j]*i] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
get_primes(MAXN);
while(true)
{
scanf("%d",&n);
if(!n) break;
for(int i = 0;i < cnt;i++)
{
int pos = lower_bound(primes,primes+cnt,n-primes[i]) - primes;
if(primes[i] + primes[pos] == n) {
printf("%d = %d + %d\n",n,primes[i],primes[pos]);
break;
}
}
}
return 0;
}
1293. 夏洛克和他的女朋友 - AcWing题库
题意:当一个数是另一个数的质因子时,两个数颜色不同,问最少染色次数
最多两个颜色,是个二分图。质数一个集合,合数一个集合,边只是质数连向合数。
需要特判全是质数的情况,其余质数染一种颜色,合数染一种颜色。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1e5+50;
int n;
bool vis[MAXN];
int primes[MAXN], cnt;
void get_primes(int n)
{
for(int i = 2;i <= n;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
get_primes(1e5+10);
scanf("%d",&n);
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("2 ");
else printf("1 ");
}
}
printf("\n");
return 0;
}
196. 质数距离 - AcWing题库
性质:对于每个合数 p p p都有一个 p \sqrt{p} p 的质因子
对于一段区间 [ L , R ] [L,R] [L,R],可以将 R \sqrt{R} R中的质数筛出来,并将其在 [ L , R ] [L,R] [L,R]中的倍数筛为合数,其余为质数。
题意:给一段区间长度 ≤ 1 e 6 \le 1e6 ≤1e6,但区间右端点为 2 31 2^{31} 231,需要将最小相邻与最大相邻质数输出。
依照上述做法取做。
技巧:为保证数组能够存下,将 [ L , R ] [L,R] [L,R]的区间映射到 [ 0 , L − R ] [0,L-R] [0,L−R]上。注意1不是素数,需要特殊判断。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e7+50;
int cnt, primes[MAXN], l, r;
bool vis[MAXN];
void get_primes(int n)
{
memset(vis, false, sizeof(vis));
cnt = 0;
vis[1] = true;
for(int i = 2;i <=n;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
while(~scanf("%d%d",&l,&r))
{
get_primes(50000);
memset(vis, false, sizeof(vis));
for(int j = 0;j < cnt;j++)
{
ll k = primes[j];
for(ll i = max(k+k, (l + k - 1) / k * k); i <= r; i += k)
{
vis[i - l] = true;
}
}
cnt = 0;
for(int i = 0;i <= r-l;i++)
{
if(!vis[i] && i + l >= 2) primes[cnt++] = i + l;
}
int maxp = 0, minp = 1e6, maxid, minid;
if(cnt < 2) {
puts("There are no adjacent primes.");
}
else
{
for(int i = 1;i < cnt;i++)
{
if(primes[i] - primes[i-1] > maxp) {maxp = primes[i] - primes[i-1]; maxid = i;}
if(primes[i] - primes[i-1] < minp) {minp = primes[i] - primes[i-1]; minid = i;}
}
printf("%d,%d are closest, %d,%d are most distant.\n",primes[minid-1], primes[minid], primes[maxid-1], primes[maxid]);
}
}
return 0;
}
分解质因数
197. 阶乘分解 - AcWing题库
考虑1-n中p的倍数有几个。
p , 2 p , 3 p , … , ⌊ n p ⌋ p p,2p,3p,\dots,\lfloor \frac{n}{p} \rfloor p p,2p,3p,…,⌊pn⌋p ⌊ n p ⌋ \lfloor \frac{n}{p} \rfloor ⌊pn⌋个
p 2 , 2 p 2 , 3 p 2 , … , ⌊ n p 2 ⌋ p 2 p^2,2p^2,3p^2,\dots,\lfloor \frac{n}{p^2} \rfloor p^2 p2,2p2,3p2,…,⌊p2n⌋p2 ⌊ n p 2 ⌋ \lfloor \frac{n}{p^2} \rfloor ⌊p2n⌋个
s u m = ∑ ⌊ n p ⌋ + ⌊ n p 2 ⌋ + ⌊ n p 3 ⌋ + … sum = \sum \lfloor \frac{n}{p} \rfloor + \lfloor \frac{n}{p^2} \rfloor + \lfloor \frac{n}{p^3} \rfloor + \dots sum=∑⌊pn⌋+⌊p2n⌋+⌊p3n⌋+…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int MAXN = 1e6+50;
typedef long long ll;
int n;
bool vis[MAXN];
int primes[MAXN], cnt;
map<int, ll>mp;
void get_primes(int n)
{
for(int i = 2;i <= n;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
scanf("%d",&n);
get_primes(n);
for(int i = 0;i < cnt;i++)
{
ll t = primes[i], tmp = primes[i], sum = 0;
while(tmp <= n)
{
sum += n / tmp;
tmp *= t;
}
mp[t] = sum;
}
for(auto i : mp)
{
printf("%d %d\n", i.first, i.second);
}
return 0;
}
快速幂
1289. 序列的第k个数 - AcWing题库(基本应用)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
int T;
ll a, b, c, k;
const int MOD = 200907;
int qpow(int a, int k, int p)
{
int res = 1;
while(k)
{
if(k & 1) res = (ll) res * a % p;
k >>= 1;
a = (ll)a * a % p;
}
return res;
}
int main()
{
scanf("%d",&T);
while(T--)
{
ll ans;
scanf("%llu%llu%llu%llu",&a,&b,&c,&k);
if(a + c == b + b)
{
ll d = b - a;
ans = (a + (k-1) % MOD * d % MOD) % MOD;
}
else
{
ll d = b / a;
ans = a * qpow(d, k-1, MOD) % MOD;
}
printf("%llu\n", ans);
}
return 0;
}
1290. 越狱 - AcWing题库(基础排列组合问题)
a n s = m n − m ( m − 1 ) n − 1 ans = m ^ n - m (m-1)^{n-1} ans=mn−m(m−1)n−1
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MOD = 100003;
ll n, m;
int qpow(ll a, ll b)
{
int res = 1;
while(b)
{
if(b & 1) res = res * a % MOD;
b >>= 1;
a = a * a % MOD;
}
return res;
}
int main()
{
scanf("%lld%lld", &m, &n);
ll ans = (qpow(m, n) - m * qpow(m-1, n-1) % MOD + MOD) % MOD;
printf("%lld\n",ans);
return 0;
}
约数个数
1291. 轻拍牛头 - AcWing题库(简单求约数个数)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e6 + 60;
int n, cnt[MAXN], a[MAXN];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++) {
scanf("%d",&a[i]);
cnt[a[i]]++;
}
for(int i = 1;i <= n;i++)
{
if(a[i] == 1) {
printf("%d\n", cnt[1] - 1);
continue;
}
else
{
int ans = cnt[1] + cnt[a[i]] - 1;
for(int j = 2;j * j <= a[i];j++)
{
if(a[i] % j == 0 && a[i] != j * j) ans += (cnt[j] + cnt[a[i] / j]);
else if(a[i] % j == 0 && a[i] == j * j) ans += cnt[j];
}
printf("%d\n", ans);
}
}
return 0;
}
1294. 樱花 - AcWing题库(求约数个数)
给定一个整数 n,求有多少正整数数对 (x,y) 满足
1
x
+
1
y
=
1
n
!
\frac1x+\frac1y=\frac1{n!}
x1+y1=n!1
y
=
x
∗
n
!
x
−
n
!
y
=
(
x
+
n
!
−
n
!
)
∗
n
!
x
−
n
!
y
=
n
!
+
(
n
!
)
2
x
−
n
!
y = \frac{x * n!}{x - n!} \\ y = \frac{(x+n!-n!) * n!}{x - n!} \\ y = n! + \frac{(n!)^2}{x - n!}
y=x−n!x∗n!y=x−n!(x+n!−n!)∗n!y=n!+x−n!(n!)2
所以只需要求
(
n
!
)
2
(n!)^2
(n!)2的约数个数
保证x, y大于0, 即 x , y ≥ n ! x,y\ge n! x,y≥n!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
const int MAXN = 1e6+60;
const int mod = 1e9+7;
typedef long long ll;
int n;
bool vis[MAXN];
int primes[MAXN], cnt;
map<int, ll>mp;
void get_primes(int n)
{
vis[1] = true;
for(int i = 2;i <= n;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
scanf("%d", &n);
get_primes(n);
for(int i = 0;i < cnt;i++)
{
ll t = primes[i], tmp = primes[i], sum = 0;
while(tmp <= n)
{
sum += n / tmp;
tmp *= t;
}
mp[t] = sum;
}
ll ans = 1;
for(auto i : mp)
{
ans = ans * (2 * i.second + 1) % mod;
}
printf("%lld\n", ans);
return 0;
}
🔺198. 反素数 - AcWing题库(dfs)
对于任意整数x, g ( x ) g(x) g(x)代表其约数个数
反素数的定义为:对一个整数x,都满足任意 i < x i < x i<x, g ( i ) < g ( x ) g(i) < g(x) g(i)<g(x)
求比给定n小的最大反素数
思路:
2 ∗ 1 0 9 2*10^9 2∗109最多从 2 ∗ 3 ∗ ⋯ ∗ 23 2 * 3 * \dots *23 2∗3∗⋯∗23,用到9种质数(暴搜来做)
每个因子的最大质数为30
所有质因子的次数递减,质因子越小,其所对应的次数越大。
dfs(u, last, p, s)
u表示用到第几个质数,last表示最高次数,p表示当前的数,s表示约数个数
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int n;
int maxd, number;
int primes[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
void dfs(int u, int last, int p, int s)
{
if(s > maxd || (s == maxd && p < number))
{
maxd = s;
number = p;
}
if(u == 9) return;
for(int i = 1;i <= last;i++)
{
if((ll)p * primes[u] > n) break;
p *= primes[u];
dfs(u + 1, last, p, s * (i + 1));
}
}
int main()
{
scanf("%d", &n);
dfs(0, 30, 1, 1);
printf("%d\n", number);
return 0;
}
🔺200. Hankson的趣味题 - AcWing题库(dfs优化求约数)
满足 g c d ( x , a 0 ) = a 1 l c s ( x , b 0 ) = b 1 gcd(x,a_0) = a_1 \quad lcs(x,b_0) = b_1 gcd(x,a0)=a1lcs(x,b0)=b1 x的个数
可以考虑枚举 b 1 b_1 b1的所有约数,满足题意 g c d ( x , a 0 ) = a 1 , x ∗ b 0 / g c d ( x , b 0 ) = b 1 gcd(x,a_0) = a_1, x * b_0 /gcd(x,b_0) = b_1 gcd(x,a0)=a1,x∗b0/gcd(x,b0)=b1
由于时间复杂度的限制,需要优化枚举 b 1 b_1 b1约数的方式
先线性筛,枚举所有质数,对d分解质因数,然后合成所有约数
dfs(u, num)
表示枚举到第u个质数,乘积为num
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 50010;
int T;
int a, b, c, d;
bool vis[MAXN];
int primes[MAXN], cnt, dcnt;
int divisor[MAXN], fcnt;
struct factor{
int p, s;
}Factor[1602];
void init(int n)
{
for(int i = 2;i <= n;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
}
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
void dfs(int u, int num)
{
if(u == fcnt)
{
divisor[dcnt++] = num;
return;
}
for(int i = 0;i <= Factor[u].s;i++)
{
dfs(u + 1, num);
num = num * Factor[u].p;
}
}
int main()
{
init(MAXN-2);
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d%d", &a, &b, &c, &d);
fcnt = 0;
int t = d;
for(int i = 0;primes[i] * primes[i] <= t;i++)
{
if(t % primes[i] == 0)
{
int s = 0;
while(t % primes[i] == 0)
{
t /= primes[i];
s++;
}
Factor[fcnt++] = {primes[i], s};
}
}
if(t > 1) Factor[fcnt++] = {t, 1};
//for(int i = 0;i < fcnt;i++) printf("%d %d\n", Factor[i].p, Factor[i].s);
dcnt = 0;
dfs(0, 1);
/*
for(int i = 0;i < dcnt;i++) printf("%d ", divisor[i]);
printf("\n");
*/
int res = 0;
for(int i = 0;i < dcnt;i++)
{
int x = divisor[i];
if(gcd(a, x) == b && (ll)c * x / gcd(c, x) == d) {
//printf("%d ",x);
res++;
}
}
printf("%d\n", res);
}
return 0;
}
欧拉函数
201. 可见的点 - AcWing题库(模板题)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1010;
int T, n;
int primes[MAXN], euler[MAXN], cnt;
bool vis[MAXN];
void get_euler(int n)
{
euler[1] = 1;
for(int i = 2;i <= n;i++)
{
if(!vis[i])
{
primes[cnt++] = i;
euler[i] = i - 1;
}
for(int j = 0;primes[j] * i <= n;j++)
{
int t = i * primes[j];
vis[t] = true;
if(i % primes[j] == 0)
{
euler[t] = euler[i] * primes[j];
break;
}
euler[t] = euler[i] * (primes[j] - 1);
}
}
}
int main()
{
get_euler(1005);
scanf("%d", &T);
for(int i = 1;i <= T;i++)
{
scanf("%d", &n);
int res = 0;
for(int j = 1;j <= n;j++) res += euler[j];
printf("%d %d %d\n",i ,n, res * 2 + 1);
}
return 0;
}
220. 最大公约数 - AcWing题库(模板题)
题意:求小于n的整数对(x,y)满足 g c d ( x , y ) gcd(x,y) gcd(x,y)为素数
考虑小于n的素数 p r i m e s [ i ] primes[i] primes[i],保证 g c d ( x / p r i m e s [ i ] , y / p r i m e s [ i ] ) = 1 gcd(x/primes[i],y/primes[i]) =1 gcd(x/primes[i],y/primes[i])=1
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1e7+10;
typedef long long ll;
int euler[MAXN], primes[MAXN], cnt;
bool vis[MAXN];
int n;
ll sum[MAXN];
void get_euler(int n)
{
euler[1] = 1;
for(int i = 2;i <= n;i++)
{
if(!vis[i])
{
primes[cnt++] = i;
euler[i] = i - 1;
}
for(int j = 0;i * primes[j] <= n;j++)
{
int t = i * primes[j];
vis[t] = true;
if(i % primes[j] == 0)
{
euler[t] = euler[i] * primes[j];
break;
}
euler[t] = euler[i] * (primes[j] - 1);
}
}
}
void init()
{
for(int i = 1;i <= MAXN - 5;i++)
{
sum[i] = sum[i-1] + euler[i];
}
}
int main()
{
get_euler(MAXN - 2);
init();
scanf("%d", &n);
ll res = 0;
for(int i = 0;primes[i] <= n;i++)
{
int t = n / primes[i];
res = res + sum[t] * 2 - 1;
}
printf("%lld\n", res);
return 0;
}
同余
求解同余方程: a x ≡ b ( m o d d ) ax \equiv b (mod \ d) ax≡b(mod d)
等价于求解 a x + b y = c ax+by=c ax+by=c, d = gcd ( a , b ) d=\gcd(a,b) d=gcd(a,b)
1.该方程有整数解的充分必要条件是 d ∣ c d|c d∣c,即 c c c被 g c d ( a , b ) gcd(a,b) gcd(a,b)整除,且若有解,共有d个解
2.若 a x + b y = d ax+by=d ax+by=d的一组特解为( x 0 ′ , y 0 ′ x_0',y_0' x0′,y0′),那么 a x + b y = c ax+by=c ax+by=c的一组特解为 ( x 0 = c d x 0 ′ , y 0 = c d y 0 ′ ) (x_0=\frac{c}{d}x_0',y_0=\frac{c}{d}y_0') (x0=dcx0′,y0=dcy0′)
3.若 a x + b y = c ax+by=c ax+by=c的一组特解为 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),那么方程的通解为 ( x = x 0 + k b d , y = y 0 − k b d ) k ∈ Z (x=x_0+k \frac{b}{d},y=y_0-k \frac{b}{d}) \quad k∈Z (x=x0+kdb,y=y0−kdb)k∈Z
4.该方程在 [ 0 , b d − 1 ] [0,\frac{b}{d}-1] [0,db−1]上有唯一解
203. 同余方程 - AcWing题库(模板题)
求a关于mod b下的逆元
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int exgcd(int a, int b, int &x, int &y)
{
if(!b)
{
x = 1; y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a/b) * x;
return d;
}
int main()
{
int a, b, x, y;
scanf("%d%d", &a, &b);
exgcd(a, b, x, y);
printf("%d\n", (x + b) % b);
return 0;
}
222. 青蛙的约会 - AcWing题库(同余方程)
起点的距离差为 b − a b-a b−a,速度差为 m − n m-n m−n。
求解 ( m − n ) x − y l = b − a (m-n)x-yl = b-a (m−n)x−yl=b−a
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll x, y, m, n, l, a, b;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(!b)
{
x = 1; y = 0;
return a;
}
ll d = exgcd(b, a%b, y, x);
y -= (a / b) * x;
return d;
}
int main()
{
scanf("%lld%lld%lld%lld%lld", &a, &b, &m, &n, &l);
ll d = exgcd(m-n, l, x, y);
if((b - a) % d) puts("Impossible");
else{
x *= (b - a) / d;
ll t = abs(l / d);
printf("%lld\n", (x % t + t) % t);
}
return 0;
}
202. 最幸运的数字 - AcWing题库(同余扩展)
题意:给定一个L,问最少要几个连在一起的8能把他整除。
L ∣ x 个 8 L ∣ 8 ∗ x 个 1 L ∣ 8 ∗ x 个 9 9 L ∣ 8 ∗ ( 1 0 x − 1 ) 9 记 d 为 gcd ( 8 , L ) 9 ∗ L d ∣ 1 0 x − 1 满 足 1 0 x ≡ 1 ( m o d 9 ∗ L d ) 的 最 小 x 若 gcd ( 10 , 9 ∗ L d ) ≠ 1 无 解 记 9 ∗ L d = C 根 据 欧 拉 定 理 1 0 φ ( C ) ≡ 1 ( m o d C ) 但 φ ( C ) 并 不 是 最 小 的 x 满 足 x ∣ φ ( C ) L|x个8\\ L|8*x个1 \\ L|\frac{8 * x个9}{9}\\ L|\frac{8 * (10 ^ x - 1)}{9}\\ 记d为 \gcd(8, L)\\ \frac{9*L}{d} | 10^x-1 \\ 满足\ 10^x \equiv 1 (mod \ \frac{9*L}{d}) 的最小x\\ 若\gcd(10,\frac{9*L}{d}) \ne 1 无解\\ 记 \frac{9*L}{d} = C \\ 根据欧拉定理 \ 10^{\varphi(C)} \equiv 1 (mod \ C) \\ 但 \varphi(C)并不是最小的x \\ 满足 x | \varphi(C) \\ L∣x个8L∣8∗x个1L∣98∗x个9L∣98∗(10x−1)记d为gcd(8,L)d9∗L∣10x−1满足 10x≡1(mod d9∗L)的最小x若gcd(10,d9∗L)=1无解记d9∗L=C根据欧拉定理 10φ(C)≡1(mod C)但φ(C)并不是最小的x满足x∣φ(C)
采用反证法证明:
x
∣
φ
(
C
)
x|\varphi(C)
x∣φ(C)
若
φ
(
C
)
=
q
x
+
r
(
0
<
r
<
x
)
1
0
φ
(
C
)
≡
1
(
m
o
d
C
)
1
0
x
≡
1
(
m
o
d
C
)
1
0
q
r
≡
1
(
m
o
d
C
)
1
0
r
≡
1
(
m
o
d
C
)
不
满
足
x
为
最
小
的
所
以
x
∣
φ
(
C
)
若\varphi(C) = qx+r \ \ (0 < r < x) \\ 10^{\varphi(C)} \equiv 1 (mod \ C)\\ 10^x \equiv 1 (mod \ C) \\ 10^{qr} \equiv 1 (mod \ C) \\ 10^r \equiv 1 (mod \ C) \\ 不满足x为最小的 \\ 所以x|\ \varphi(C)
若φ(C)=qx+r (0<r<x)10φ(C)≡1(mod C)10x≡1(mod C)10qr≡1(mod C)10r≡1(mod C)不满足x为最小的所以x∣ φ(C)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
ll L;
ll qmul(ll a, ll b, ll mod) // 需要用到64位乘法,不然爆ll
{
ll res = 0;
while(b)
{
if(b & 1) res = (res + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return res;
}
ll qpow(ll a, ll b, ll mod) // 可能快速幂的过程中爆ll,mod太大
{
ll res = 1;
while(b)
{
if(b & 1) res = qmul(res, a, mod);
b >>= 1;
a = qmul(a, a, mod);
}
return res;
}
int main()
{
int id = 1;
while(cin >> L)
{
if(!L) break;
ll d = 1;
while(L % (d * 2) == 0 && d < 8) d *= 2;
ll C = L / d * 9;
if(C % 2 == 0 || C % 5 == 0) {
printf("Case %d: 0\n", id++);
continue;
}
ll c = C;
ll res = C;
for(ll i = 2;i * i <= C;i++)
{
if(C % i == 0)
{
res = res / i * (i-1);
while(C % i == 0) C /= i;
}
}
if(C > 1) res = res / C * (C - 1);
// printf("res = %lld\n", res);
ll ans = res;
for(ll i = 1;i * i <= res;i++)
{
if(res % i == 0)
{
if(qpow(10, i, c) == 1) ans = min(ans, i);
if(qpow(10, res / i, c) == 1) ans = min(ans, res / i);
}
}
printf("Case %d: %lld\n", id++, ans);
}
return 0;
}
中国剩余定理
x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) … x ≡ a n ( m o d m n ) ( m 1 … m n ) 两 两 互 质 M = m 1 m 2 … m n 令 M i = M m i t i 是 M i 关 于 m i 的 逆 元 即 t i M i ≡ 1 ( m o d m i ) x = ∑ i = 1 n a i M i t i x \equiv a_1 (mod \ m_1) \\ x \equiv a_2 (mod \ m_2) \\ \dots \\ x \equiv a_n(mod \ m_n)\\ (m_1 \dots m_n)两两互质 \\ M = m_1m_2 \dots m_n \\ 令M_i= \frac{M}{m_i} \\ t_i是M_i关于m_i的逆元 \quad 即t_iM_i\equiv1(mod\ m_i)\\ x = \sum^{n}_{i = 1}a_iM_it_i \\ x≡a1(mod m1)x≡a2(mod m2)…x≡an(mod mn)(m1…mn)两两互质M=m1m2…mn令Mi=miMti是Mi关于mi的逆元即tiMi≡1(mod mi)x=i=1∑naiMiti
1298. 曹冲养猪 - AcWing题库(模板题)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int n;
ll a[12], b[12];
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a%b, y, x);
y -= (a / b) * x;
return d;
}
int main()
{
scanf("%d", &n);
ll M = 1;
for(int i = 0;i < n;i++)
{
scanf("%lld%lld", &a[i], &b[i]);
M *= a[i];
}
ll res = 0;
for(int i = 0;i < n;i++)
{
ll m = M / a[i];
ll ti, x;
ll t = exgcd(m, a[i], ti, x);
res = res + b[i] * m *ti;
}
printf("%lld\n", (res % M + M) % M);
return 0;
}
矩阵乘法
矩阵快速幂求斐波那契数列
$$
\left(
\begin{array}{l}
& Fibonacci[i] &\
& Fibonacci[i-1] &
\end{array}
\right)
\left(
\begin{array}{l}
& 1 & 1 &\
& 1 & 0 &
\end{array}
\right)
*
\left(
\begin{array}{l}
& Fibonacci[i-1] &\
& Fibonacci[i-2] &
\end{array}
\right)
$$
有递推关系可得
$$
\left(
\begin{array}{l}
& Fibonacci[i] &\
& Fibonacci[i-1] &
\end{array}
\right)
\left(
\begin{array}{l}
& 1 & 1 &\
& 1 & 0 &
\end{array}
\right)^{i-1}
*
\left(
\begin{array}{l}
& Fibonacci[1] &\
& Fibonacci[2] &
\end{array}
\right)
$$
此时需要求矩阵的快速幂
常见递推式:
①
f
[
n
]
=
a
f
[
n
−
1
]
+
b
[
f
−
2
]
+
c
f[n] = af[n-1]+b[f-2]+c
f[n]=af[n−1]+b[f−2]+c
$$
\left(
\begin{array}{l}
& F[i] &\
& F[i-1] & \
& C & \
\end{array}
\right)
\left(
\begin{array}{l}
& a & b & 1 &\
& 1 & 0 & 0 &\
& 0 & 0 & 1&
\end{array}
\right)
*
\left(
\begin{array}{l}
& F[i-1] &\
& F[i-2] &\
& C &
\end{array}
\right)
KaTeX parse error: Can't use function '$' in math mode at position 4: ② $̲f[n] = c^n-f[n-…
\left(
\begin{array}{l}
& F[i] &\
& c^n &
\end{array}
\right)
\left(
\begin{array}{l}
& -1 & c &\
& 0 & c &
\end{array}
\right)
*
\left(
\begin{array}{l}
& F[i-1] &\
& c^{i-1} &
\end{array}
\right)
$$
1303. 斐波那契前 n 项和 - AcWing题库(模板)
$$
\left(
\begin{array}{l}
& F[i] &\
& F[i+1] & \
& S[i] & \
\end{array}
\right)
\left(
\begin{array}{l}
& 0 & 1 & 0 &\
& 1 & 1 & 0 &\
& 0 & 1 & 1 &
\end{array}
\right)
*
\left(
\begin{array}{l}
& F[i-1] &\
& F[i] &\
& S[i-1] &
\end{array}
\right)
$$
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int n, m;
void mul(ll c[3], ll a[3][3], ll b[3]) // c = a * b
{
ll tmp[3] = {0};
for(int i = 0;i < 3;i++)
{
for(int j = 0;j < 3;j++)
{
tmp[i] = (tmp[i] + a[i][j] * b[j]) % m;
}
}
memcpy(c, tmp, sizeof(tmp));
}
void mul(ll c[][3], ll a[][3], ll b[][3]) // c = a * b
{
ll tmp[3][3] = {0};
for(int i = 0;i < 3;i++)
{
for(int j = 0;j < 3;j++)
{
for(int k = 0;k < 3;k++)
{
tmp[i][j] = (tmp[i][j] + a[i][k] * b[k][j]) % m;
}
}
}
memcpy(c, tmp, sizeof(tmp));
}
int main()
{
scanf("%d%d", &n, &m);
ll f1[3] = {1, 1, 1};
ll a[3][3] = {
{0, 1, 0},
{1, 1, 0},
{0, 1, 1},
};
n--;
while(n)
{
if(n & 1) mul(f1, a, f1); // res = res * a;
mul(a, a, a); // a = a * a;
n >>= 1;
}
printf("%lld\n", f1[2]);
return 0;
}
1304. 佳佳的斐波那契 - AcWing题库(构造新的数列)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L1zo72Ox-1629966898830)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210810101929631.png)]
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n, m;
void mul(ll c[][4], ll a[][4], ll b[][4])
{
ll tmp[4][4] = {0};
for(int i = 0;i < 4;i++)
{
for(int j = 0;j < 4;j++)
{
for(int k = 0;k < 4;k++)
{
tmp[i][j] = (tmp[i][j] + a[i][k] * b[k][j]) % m;
}
}
}
memcpy(c, tmp, sizeof(tmp));
}
int main()
{
cin >> n >> m;
ll f1[4][4] = {1, 1, 0, 1}; // 简化代码,只要写一个mul
ll a[4][4] = {
{1, 1, 0, 1},
{1, 0, 0, 0},
{0, 0, 1, 0},
{0, 0, 1, 1},
};
ll tmp = n-1;
while(tmp)
{
if(tmp & 1) mul(f1, f1, a);
mul(a, a, a);
tmp >>= 1;
}
printf("%lld\n", ((f1[0][3] * n - f1[0][2]) % m + m) % m);
return 0;
}
组合计数
加法原理
乘法原理
组合数
排列数
容斥原理
1307. 牡牛和牝牛 - AcWing题库
题意:由0和1组成的长度为n的字符串,满足任意两个1之间至少有k个0
设 f ( i ) f(i) f(i)为以1结尾,长度为 i i i的所有方案数。
f ( i ) = ∑ j = 1 i − k − 1 f ( j ) f(i) = \sum_{j=1}^{i-k-1}f(j) f(i)=∑j=1i−k−1f(j)
a n s = ∑ i = 1 n f ( i ) ans = \sum_{i=1}^nf(i) ans=∑i=1nf(i)
维护前缀和即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e5+50;
const int mod = 5000011;
int n, k;
int f[MAXN], sum[MAXN];
int main()
{
cin >> n >> k;
f[0] = sum[0] = 1;
for(int i = 1;i <= n;i++)
{
f[i] = sum[max(0, i - k - 1)];
sum[i] = (sum[i - 1] + f[i]) % mod;
}
printf("%d\n", sum[n]);
return 0;
}
1308. 方程的解 - AcWing题库(隔板法)
KaTeX parse error: Undefined control sequence: \C at position 1: \̲C̲_{gx-1}^{k-1}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 1000;
const int N = 150;
ll x, k;
int f[1000][100][N];
ll qmi(ll a, ll b)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
void add(int c[], int a[], int b[]) // 因为没有取模,需要用到高精度
{
for (int i = 0, t = 0; i < N; i ++ )
{
t += a[i] + b[i];
c[i] = t % 10;
t /= 10;
}
}
int main()
{
cin >> k >> x;
x %= 1000;
ll gx = qmi(x, x);
// C(gx-1, k-1);
for (int i = 0; i < gx; i ++ )
for (int j = 0; j <= i && j < k; j ++ )
if (!j) f[i][j][0] = 1;
else add(f[i][j], f[i - 1][j], f[i - 1][j - 1]); // f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
int *g = f[gx - 1][k - 1];
int i = N - 1;
while (!g[i]) i -- ;
while (i >= 0) cout << g[i -- ];
return 0;
}
1309. 车的放置 - AcWing题库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H32DcsFm-1629966898833)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210812115610967.png)]
答案为KaTeX parse error: Undefined control sequence: \C at position 22: …\sum_{i=1}^{k} \̲C̲_a^i A_b^i\C_{a…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 5010; // 注意逆元多处理几位(注意范围)
const int mod = 100003;
int fact[MAXN], infact[MAXN];
int a, b, c, d, k;
int qmi(int a, int b)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll)res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
void init()
{
fact[0] = infact[0] = 1;
for(int i = 1;i <= MAXN - 5;i++)
{
fact[i] = (ll)fact[i - 1] * i % mod;
infact[i] = (ll)infact[i - 1] * qmi(i, mod - 2) % mod;
}
}
ll C(int m, int n)
{
if(m < n) return 0;
return (ll)fact[m] * infact[n] % mod * infact[m - n] % mod;
}
ll A(int m, int n)
{
if(m < n) return 0;
return (ll)fact[m] * infact[m - n] % mod;
}
int main()
{
cin >> a >> b >> c >> d >> k;
init();
ll ans = 0;
for(int i = 0;i <= k;i++)
{
ans = (ans + (ll)C(a, i) * A(b, i) % mod * C(a+c-i, k-i) % mod * A(d, k-i) % mod) % mod;
}
printf("%lld\n", ans);
return 0;
}
1310. 数三角形 - AcWing题库
首先n++, m++;转化为格点数,我们需要从 n×m个格点中选出 3 个合法格点构成三角形,那么显然我们只需要将 C n m 3 C^3_{nm} Cnm3减去不合法的情况(即三点共线的情况)。
我们将三点共线的斜率分为三种情况分别统计:
-
斜率不存在(即竖直): m C n 3 mC^3_n mCn3
-
斜率为 0(即水平): n C m 3 nC^3_m nCm3
-
斜率存在且不为 0,斜率为正,与斜率为负是对称的,那么只考虑前者即可。
首先n–, m–; 还原为长度。
我们在 n×m 的矩形中,枚举底为j,高为i的直角三角形,共有 ( n − i + 1 ) ( m − j + 1 ) (n−i+1)(m−j+1) (n−i+1)(m−j+1) 种(当然,我们只考虑斜边斜率大于 0 的情况)这样的直角三角形,其斜边上的两个端点一定在格点上,我们只需要考察斜边上除了端点,还存在的格点的数量,结论有 g c d ( i , j ) − 1 gcd(i,j)−1 gcd(i,j)−1 个。
那么这个三角形的斜边对三点共线的贡献即为: ( g c d ( i , j ) − 1 ) (gcd(i,j)−1) (gcd(i,j)−1)种。
总结:共 2 ( n − i + 1 ) ( m − j + 1 ) ( g c d ( i , j ) − 1 ) 2(n−i+1)(m−j+1)(gcd(i,j)−1) 2(n−i+1)(m−j+1)(gcd(i,j)−1)种
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n, m;
ll gcd(ll a, ll b)
{
return b ? gcd(b, a%b) : a;
}
ll C_3(ll a)
{
return a * (a - 1) * (a - 2) / 6;
}
int main()
{
cin >> n >> m;
n ++, m ++;
ll ans = C_3(n*m);
ans = ans - m * C_3(n);
ans = ans - n * C_3(m);
n--, m--;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
ans = ans - 2 * (n - i + 1) * (m - j + 1) * (gcd(i, j) - 1);
}
}
printf("%lld\n", ans);
return 0;
}
AcWing 1312. 序列统计 - AcWing
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lGdDqSd2-1629966898834)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210812210215837.png)]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 1e6+3;
int T;
ll l, r, n;
int qmi(int a, int k, int p)
{
int res = 1;
while(k)
{
if(k & 1) res = (ll) res * a % p;
a = (ll)a * a % p;
k >>= 1;
}
return res;
}
int C(int a, int b, int p)
{
if(a < b) return 0;
ll x = 1, y = 1;
for(int i = a, j = 1;j <= b;i--, j++)
{
x = (ll) x * i % p;
y = (ll) y * j % p;
}
return x * (ll)qmi(y, p-2, p) % p;
}
int lucas(ll a, ll b, int p)
{
if(a < p && b < p) return C(a, b, p);
return (ll)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
int main()
{
scanf("%d", &T);
while(T--)
{
cin >> n >> l >> r;
int ans = (ll)(lucas(r - l + n + 1, r - l + 1, mod) - 1 + mod) % mod;
printf("%d\n", ans);
}
return 0;
}
1315. 网格 - AcWing题库(卡特兰数应用)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xg3PmFm2-1629966898836)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210812215221402.png)]
依据卡塔兰数的推导,找出(n,m)关于y=x+1的对称点(m−1,n+1),每一条非法路径都对应一条(0,0)到(m−1,n+1)的路径,因此合法路径=总路径数-非法路径,即 C n + m n − C n + m m − 1 C^n_{n+m}−C^{m−1}_{n+m} Cn+mn−Cn+mm−1
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int primes[N], cnt;
bool st[N];
int a[N], b[N];
void init(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] * i <= n; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int get(int n, int p)
{
int s = 0;
while (n) s += n / p, n /= p;
return s;
}
void mul(int r[], int &len, int x)
{
int t = 0;
for (int i = 0; i < len; i ++ )
{
t += r[i] * x;
r[i] = t % 10;
t /= 10;
}
while (t)
{
r[len ++ ] = t % 10;
t /= 10;
}
}
void sub(int a[], int al, int b[], int bl)
{
for (int i = 0, t = 0; i < al; i ++ )
{
a[i] -= t + b[i];
if (a[i] < 0) a[i] += 10, t = 1;
else t = 0;
}
}
int C(int x, int y, int r[N])
{
int len = 1;
r[0] = 1;
for (int i = 0; i < cnt; i ++ )
{
int p = primes[i];
int s = get(x, p) - get(y, p) - get(x - y, p);
while (s -- ) mul(r, len, p);
}
return len;
}
int main()
{
init(N - 1);
int n, m;
cin >> n >> m;
int al = C(n + m, m, a);
int bl = C(n + m, n + 1, b);
sub(a, al, b, bl);
int k = al - 1;
while (!a[k] && k > 0) k -- ;
while (k >= 0) printf("%d", a[k -- ]);
return 0;
}
1316. 有趣的数列 - AcWing题库(卡特兰数)
从1-n放置,当前放置的奇数项个数不能小于偶数项个数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2jh5QUK6-1629966898838)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210812222227087.png)]
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 2000010;
int n, p;
int primes[N], cnt;
bool st[N];
void init(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] * i <= n; j ++ )
{
st[i * primes[j]] = true;
if (i % primes[j] == 0) break;
}
}
}
int qmi(int a, int k)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int get(int n, int p)
{
int s = 0;
while (n)
{
s += n / p;
n /= p;
}
return s;
}
int C(int a, int b)
{
int res = 1;
for (int i = 0; i < cnt; i ++ )
{
int prime = primes[i];
int s = get(a, prime) - get(b, prime) - get(a - b, prime);
res = (LL)res * qmi(prime, s) % p;
}
return res;
}
int main()
{
scanf("%d%d", &n, &p);
init(n * 2);
cout << (C(n * 2, n) - C(n * 2, n - 1) + p) % p << endl;
return 0;
}
高斯消元(待补)
容斥原理
214. Devu和鲜花 - AcWing题库
题意:给n个盒子,每个盒子中有 A i A_i Ai枝花,要从这些盒子中一共选出m朵。
若每个盒子❀的上限无限:则满足 a 1 + ⋯ + a n = m ( a k > = 0 ) a_1+\dots+a_n=m \quad (a_k >= 0) a1+⋯+an=m(ak>=0)
根据插板法答案为:KaTeX parse error: Undefined control sequence: \C at position 1: \̲C̲_{m+n-1}^{n-1}
需要扣除不满足题意的,若 a 1 > A 1 a_1>A_1 a1>A1,则满足 a 1 + ⋯ + a n + ( A 1 + 1 ) = m a_1+\dots+a_n+(A_1+1)=m a1+⋯+an+(A1+1)=m
根据插板法答案为KaTeX parse error: Undefined control sequence: \C at position 1: \̲C̲_{m+n-1-(A_1+1)…
根据容斥原理:KaTeX parse error: Undefined control sequence: \C at position 7: ans = \̲C̲_{m+n-1}^{n-1} …
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 25, mod = 1e9+7;
ll a[N];
ll n, m;
ll ret = 1;
ll qmi(ll a, ll b, ll MOD)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % MOD;
b >>= 1;
a = a * a % MOD;
}
return res;
}
ll C(ll a, ll b)
{
if(a < b) return 0; // 记住判断这一行
ll res = 1;
for(ll i = a;i > a-b;i--)
{
res = i % mod * res % mod;
}
res = res * ret % mod;
return res;
}
int main()
{
scanf("%lld%lld", &n, &m);
for(int i = 0;i < n;i++) scanf("%lld", &a[i]);
for(int i = 1;i <= n-1;i++) ret = ret * i % mod;
ret = qmi(ret, mod-2, mod);
ll ans = 0;
for(int i = 0;i < 1 << n;i++)
{
ll u = n + m - 1, v = n - 1;
int sign = 1;
for(int j = 0;j < n;j++)
if(i >> j & 1)
{
sign *= -1;
u -= (a[j] + 1);
}
ans = (ans + sign * C(u, v)) % mod;
}
printf("%lld\n", (ans + mod) % mod);
return 0;
}
概率与数学期望
217. 绿豆蛙的归宿 - AcWing题库(概率DP)
题意:给定一个有向完全图,问从起点走到终点路径的数学期望
E ( a x + b y ) = a E ( x ) + b E ( y ) E(ax+by) = aE(x)+bE(y) E(ax+by)=aE(x)+bE(y)
f [ i ] f[i] f[i]为 i i i跳到 N N N的平均长度
f [ N ] = 0 f[N]=0 f[N]=0 , 答案为 f [ 1 ] f[1] f[1]
若 i i i指向 x 1 , … , x k x_1, \dots ,\ x_k x1,…, xk
E [ i ] = E ( 1 k ∗ ( x 1 + w 1 ) + ⋯ + 1 k ( x k + w k ) ) E[i] = E(\frac{1}{k}*(x_1+w_1)+\dots+\frac{1}{k}(x_k+w_k)) E[i]=E(k1∗(x1+w1)+⋯+k1(xk+wk))
E ( i ) = 1 k ( w 1 + ⋯ + w k ) + 1 k ( E ( x 1 ) + ⋯ + E ( x k ) ) E(i)=\frac{1}{k}(w_1+\dots+ w_k)+\frac{1}{k}(E(x_1)+\dots+E(x_k)) E(i)=k1(w1+⋯+wk)+k1(E(x1)+⋯+E(xk))
f [ i ] = ∑ i = 1 k 1 k ( f [ x i ] + w i ) f[i]=\sum_{i=1}^k\frac{1}{k}(f[x_i]+w_i) f[i]=∑i=1kk1(f[xi]+wi)
问题转化为拓扑序从后往前推一遍
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e5+50, M = 2e5+50;
double f[N];
int head[N], d[N];
int n, m, id;
struct Edge{
int to, next, w;
}E[M];
inline void addedge(int u, int v, int w)
{
E[id].to = v;
E[id].w = w;
E[id].next = head[u];
head[u] = id++;
return ;
}
double dp(int x)
{
if(f[x] >= 0) return f[x];
f[x] = 0;
for(int i = head[x];i != -1;i = E[i].next)
{
int p = E[i].to, w = E[i].w;
f[x] += (dp(p) + 1.0 * w) / d[x];
}
return f[x];
}
int main()
{
memset(head, -1, sizeof(head));
memset(f, -1, sizeof(f));
scanf("%d%d", &n, &m);
for(int i = 1;i <= m;i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
d[u]++;
}
double ans = dp(1);
printf("%.2f\n", ans);
return 0;
}
218. 扑克牌 - AcWing题库
设 f [ a ] [ b ] [ c ] [ d ] [ x ] [ y ] f[a][b][c][d][x][y] f[a][b][c][d][x][y]表示已经拿到 a a a张黑桃, b b b张红桃, c c c张梅花, d d d张方块,小王大王状态 x , y x,y x,y其中 x = = 0 x==0 x==0表示放到♠里,1表示放到♥里,2表示放到♣里,3放到♦里,4表示没有拿到的期望翻开次数
我们可以通过计算牌的数量 s u m = a + b + c + d + ( x ≠ 4 ) + ( y ≠ 4 ) sum=a+b+c+d+(x\ne4)+(y\ne4) sum=a+b+c+d+(x=4)+(y=4)
剩下牌的数量 S u m = 54 − s u m Sum=54-sum Sum=54−sum
f [ a ] [ b ] [ c ] [ d ] [ x ] [ y ] = 13 − a S u m f [ a + 1 ] [ b ] [ c ] [ d ] [ x ] [ y ] + 13 − b S u m f [ a ] [ b + 1 ] [ c ] [ d ] [ x ] [ y ] + 13 − c S u m f [ a ] [ b ] [ c + 1 ] [ d ] [ x ] [ y ] + 13 − d S u m f [ a ] [ b ] [ c ] [ d + 1 ] [ x ] [ y ] + min 0 ≤ i ≤ 3 f [ a ] [ b ] [ c ] [ d ] [ i ] [ y ] ( x = = 4 ) + min 0 ≤ j ≤ 3 f [ a ] [ b ] [ c ] [ d ] [ x ] [ j ] ( y = = 4 ) f[a][b][c][d][x][y]=\frac{13-a}{Sum}f[a+1][b][c][d][x][y]+\frac{13-b}{Sum}f[a][b+1][c][d][x][y]+\frac{13-c}{Sum}f[a][b][c+1][d][x][y]+ \\ \frac{13-d}{Sum}f[a][b][c][d+1][x][y]+\min_{0 \le i \le 3}{f[a][b][c][d][i][y]}(x==4)+\min_{0 \le j \le3}{f[a][b][c][d][x][j]}(y==4) f[a][b][c][d][x][y]=Sum13−af[a+1][b][c][d][x][y]+Sum13−bf[a][b+1][c][d][x][y]+Sum13−cf[a][b][c+1][d][x][y]+Sum13−df[a][b][c][d+1][x][y]+0≤i≤3minf[a][b][c][d][i][y](x==4)+0≤j≤3minf[a][b][c][d][x][j](y==4)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 16;
int A, B, C, D;
int v[N][N][N][N][5][5];
double f[N][N][N][N][5][5];
double dp(int a, int b, int c, int d, int x, int y)
{
if(v[a][b][c][d][x][y]) return f[a][b][c][d][x][y];
v[a][b][c][d][x][y] = 1;
if(a + (x == 0) + (y == 0) >= A && b + (x == 1) + (y == 1) >= B && c + (x == 2) + (y == 2) >= C && d + (x == 3) + (y == 3) >= D) return 0;
int cnt = a + b + c + d + (x != 4) + (y != 4);
double sum = 1.0;
if(a < 13) sum += (double)(13 - a) / (54 - cnt) * dp(a+1, b, c, d, x, y);
if(b < 13) sum += (double)(13 - b) / (54 - cnt) * dp(a, b+1, c, d, x, y);
if(c < 13) sum += (double)(13 - c) / (54 - cnt) * dp(a, b, c+1, d, x, y);
if(d < 13) sum += (double)(13 - d) / (54 - cnt) * dp(a, b, c, d+1, x, y);
if(x == 4) {
double minn = 1e9;
for(int i = 0;i < 4;i++) minn = min(minn, (double)dp(a, b, c, d, i, y) / (54 - cnt));
sum += minn;
}
if(y == 4){
double minn = 1e9;
for(int i = 0;i < 4;i++) minn = min(minn, (double)dp(a, b, c, d, x, i) / (54 - cnt));
sum += minn;
}
return f[a][b][c][d][x][y] = sum;
}
int main()
{
scanf("%d%d%d%d", &A, &B, &C, &D);
if(max(0, A-13) + max(0, B-13) + max(0, C-13) + max(0, D-13) > 2) {
puts("-1.000");
}
else {
double ans = dp(0, 0, 0, 0, 4, 4);
printf("%.3f\n", ans);
}
return 0;
}
博弈论
1319. 移棋子游戏 - AcWing题库(sg函数模板题)
题意:给定k个棋子,一个有向无环图,无法操作(移动棋子)时输,问先手胜或负。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<unordered_set>
using namespace std;
const int N = 2020, M = 6060;
struct Edge{
int to, next;
}edge[M];
int head[N], f[N];
int n, m, k, id;
inline void addedge(int u, int v)
{
edge[id].to = v;
edge[id].next = head[u];
head[u] = id++;
return;
}
int sg(int w)
{
if(f[w] != -1) return f[w];
unordered_set<int>S;
for(int i = head[w];i != -1;i = edge[i].next)
{
int p = edge[i].to;
S.insert(sg(p));
}
for(int i = 0;;i++)
{
if(S.count(i) == 0)
{
f[w] = i;
return f[w];
}
}
}
int main()
{
memset(head, -1, sizeof(head));
memset(f, -1, sizeof(f));
scanf("%d%d%d", &n, &m, &k);
for(int i = 1;i <= m;i++)
{
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
}
int ans = 0;
for(int i = 1;i <= k;i++)
{
int w;
scanf("%d", &w);
ans ^= sg(w);
}
if(ans) puts("win");
else puts("lose");
return 0;
}
1321. 取石子 - AcWing题库
简单情况:所有堆的石子个数>1
设
b
=
堆
数
+
石
子
总
数
−
1
b=堆数+石子总数−1
b=堆数+石子总数−1
先手必胜 <=> b是奇数
对于任何一个奇数,一定存在一个偶数后继
对于任何一个偶数,所有后继必然是奇数
又当
b
=
1
b=1
b=1时,有
b
=
1
(
堆
数
)
+
1
(
石
子
总
数
)
−
1
=
1
b=1(堆数)+1(石子总数)−1=1
b=1(堆数)+1(石子总数)−1=1则最终状态一定是奇数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7pveRpH-1629966898839)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210816215223576.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8vza65OH-1629966898840)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210816210559389.png)]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M = 55, N = 50500;
int T, n;
int f[M][N];
int dp(int a, int b) // a是 石子为1的个数,b是 其他堆石子+堆数-1
{
int &v = f[a][b];
if(v != -1) return v;
if(!a) return v = b % 2;
if(b == 1) return dp(a + 1, 0);
if(a && !dp(a - 1, b)) return v = 1;
if(b && !dp(a, b - 1)) return v = 1; // b需要写在前面,运算顺序花里胡哨
if(a >= 2 && !dp(a - 2, b + (b ? 3 : 2))) return v = 1; // a >= 2 写在前面
if(a && b && !dp(a - 1, b + 1)) return v = 1;
return v = 0;
}
int main()
{
memset(f, -1, sizeof(f));
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
int a = 0, b = 0;
for(int i = 1;i <= n;i++)
{
int x;
scanf("%d", &x);
if(x == 1) a++;
else b += b ? x + 1 : x;
}
int u = dp(a, b);
if(u) puts("YES");
else puts("NO");
}
return 0;
}
生成函数
f ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n x n + ⋯ + ( x ∈ [ − 1 , 1 ] ) f(x) = a_0 + a_1x+a_2x^2+ \dots + a_nx^n + \dots + (x ∈ [-1,1]) f(x)=a0+a1x+a2x2+⋯+anxn+⋯+(x∈[−1,1])
乘法原理 -》 多项式的乘法
1 ( 1 − x ) n = a 0 + a 1 x + a 2 x 2 + a k x k + ⋯ + \frac{1}{(1-x)^n}=a_0+a_1x+a_2x^2+a_kx^k+\dots+ (1−x)n1=a0+a1x+a2x2+akxk+⋯+
KaTeX parse error: Undefined control sequence: \C at position 6: a_k= \̲C̲_{k+n-1}^{n-1}
eg1:
如果有1g,2g,3g的砝码,问能组成几克的砝码,且分别有几种方式
f 1 ( x ) = 1 + x f_1(x) = 1+x f1(x)=1+x
f 2 ( x ) = 1 + x 2 f_2(x) = 1 + x ^ 2 f2(x)=1+x2
f 3 ( x ) = 1 + x 3 f_3(x) = 1 + x^3 f3(x)=1+x3
f ( x ) = f 1 ( x ) f 2 ( x ) f 3 ( x ) = 1 + x + x 2 + x 3 + 2 x 4 + x 5 + x 6 f(x) = f_1(x)f_2(x)f_3(x) =1+x+x^2+x^3+2x^4+x^5+x^6 f(x)=f1(x)f2(x)f3(x)=1+x+x2+x3+2x4+x5+x6
次数代表能组成几克的砝码,系数为方案数
如:组成三克的砝码一种方式,四克的砝码两种方式
3132. 食物 - AcWing题库(模板题)
偶数个: 1 + x 2 + x 4 + ⋯ + = 1 1 − x 2 1+x^2+x^4+\dots+ = \frac{1}{1-x^2} 1+x2+x4+⋯+=1−x21
0个或一个: 1 + x = 1 − x 2 1 − x 1+x=\frac{1-x^2}{1-x} 1+x=1−x1−x2
0个,1个或两个: 1 + x + x 2 = 1 − x 3 1 − x 1+x+x^2=\frac{1-x^3}{1-x} 1+x+x2=1−x1−x3
奇数个: x + x 3 + ⋯ + = x 1 − x 2 x+x^3+\dots+=\frac{x}{1-x^2} x+x3+⋯+=1−x2x
4的倍数个: 1 + x 4 + x 8 + ⋯ + = 1 1 − x 4 1+x^4+x^8+\dots+=\frac{1}{1-x^4} 1+x4+x8+⋯+=1−x41
0,1,2,3个: 1 + x + x 2 + x 3 = 1 − x 4 1 − x 1+x+x^2+x^3=\frac{1-x^4}{1-x} 1+x+x2+x3=1−x1−x4
不超过一个: 1 + x = 1 − x 2 1 − x 1+x=\frac{1-x^2}{1-x} 1+x=1−x1−x2
3的倍数个: 1 + x 3 + x 6 + ⋯ + = 1 1 − x 3 1+x^3+x^6+\dots+ =\frac{1}{1-x^3} 1+x3+x6+⋯+=1−x31
KaTeX parse error: Undefined control sequence: \C at position 47: …1-x)^4}=\sum x*\̲C̲_{4+n-1}^{4}x^n
对每一个n,KaTeX parse error: Undefined control sequence: \C at position 6: ans =\̲C̲_{n+2}^3
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 10007;
int qmi(int a, int b)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll)res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
int inv(int x)
{
return qmi(x, mod-2);
}
int main()
{
int n;
cin >> n;
n %= mod;
int ans = n * (n+1) % mod * (n+2) % mod * inv(6) % mod;
printf("%d\n", ans);
return 0;
}
由于数据过大
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 510, mod = 10007;
char s[maxn];
typedef long long ll;
int main() {
ll N;
scanf("%s", s);
for (int i = 0; s[i]; i++) {
N = (N * 10 + s[i] - '0') % mod;
}
printf("%lld\n", N * (N + 1) * (N + 2) / 6 % mod);
return 0;
}
n=int(input())
mod=10007
print(n*(n+1)*(n+2)//6%mod)
if(a && b && !dp(a - 1, b + 1)) return v = 1;
return v = 0;
}
int main()
{
memset(f, -1, sizeof(f));
scanf("%d", &T);
while(T–)
{
scanf("%d", &n);
int a = 0, b = 0;
for(int i = 1;i <= n;i++)
{
int x;
scanf("%d", &x);
if(x == 1) a++;
else b += b ? x + 1 : x;
}
int u = dp(a, b);
if(u) puts(“YES”);
else puts(“NO”);
}
return 0;
}
## 生成函数
$f(x) = a_0 + a_1x+a_2x^2+ \dots + a_nx^n + \dots + (x ∈ [-1,1])$
乘法原理 -》 多项式的乘法
$\frac{1}{(1-x)^n}=a_0+a_1x+a_2x^2+a_kx^k+\dots+$
$a_k= \C_{k+n-1}^{n-1}$
### eg1:
如果有1g,2g,3g的砝码,问能组成几克的砝码,且分别有几种方式
$f_1(x) = 1+x$
$f_2(x) = 1 + x ^ 2$
$f_3(x) = 1 + x^3$
$f(x) = f_1(x)f_2(x)f_3(x) =1+x+x^2+x^3+2x^4+x^5+x^6$
次数代表能组成几克的砝码,系数为方案数
如:组成三克的砝码一种方式,四克的砝码两种方式
### [3132. 食物 - AcWing题库](https://www.acwing.com/problem/content/3135/)(模板题)
偶数个:$1+x^2+x^4+\dots+ = \frac{1}{1-x^2}$
0个或一个:$1+x=\frac{1-x^2}{1-x}$
0个,1个或两个:$1+x+x^2=\frac{1-x^3}{1-x}$
奇数个:$x+x^3+\dots+=\frac{x}{1-x^2}$
4的倍数个:$1+x^4+x^8+\dots+=\frac{1}{1-x^4}$
0,1,2,3个:$1+x+x^2+x^3=\frac{1-x^4}{1-x}$
不超过一个:$1+x=\frac{1-x^2}{1-x}$
3的倍数个:$1+x^3+x^6+\dots+ =\frac{1}{1-x^3}$
$f(x) = \prod f_i(x) =\frac{x}{(1-x)^4}=\sum x*\C_{4+n-1}^{4}x^n$
对每一个n,$ans =\C_{n+2}^3$
```c++
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 10007;
int qmi(int a, int b)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll)res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
int inv(int x)
{
return qmi(x, mod-2);
}
int main()
{
int n;
cin >> n;
n %= mod;
int ans = n * (n+1) % mod * (n+2) % mod * inv(6) % mod;
printf("%d\n", ans);
return 0;
}
由于数据过大
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 510, mod = 10007;
char s[maxn];
typedef long long ll;
int main() {
ll N;
scanf("%s", s);
for (int i = 0; s[i]; i++) {
N = (N * 10 + s[i] - '0') % mod;
}
printf("%lld\n", N * (N + 1) * (N + 2) / 6 % mod);
return 0;
}
n=int(input())
mod=10007
print(n*(n+1)*(n+2)//6%mod)