数学知识 进阶

本文深入探讨了数学在编程中的应用,从埃氏筛法、线性筛法到解决哥德巴赫猜想、同余方程等问题,展示了数学在算法设计中的魅力。通过实例解析了矩阵快速幂、欧拉函数、容斥原理等在求解复杂问题中的作用,揭示了数学知识在信息技术领域的广泛适用性。
摘要由CSDN通过智能技术生成

数学知识

筛素数

埃氏筛法 ( 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] nprimes[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,LR]​上。注意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,,pnp ⌊ 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,,p2np2 ⌊ 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=mnm(m1)n1

#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=xn!xn!y=xn!(x+n!n!)n!y=n!+xn!(n!)2
所以只需要求 ( n ! ) 2 (n!)^2 (n!)2的约数个数

保证x, y大于0, 即 x , y ≥ n ! x,y\ge n! x,yn!

 #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 2109​最多从 2 ∗ 3 ∗ ⋯ ∗ 23 2 * 3 * \dots *23 2323​,用到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,xb0/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) axb(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 dc,即 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=y0kdb)kZ

4.该方程在 [ 0 , b d − 1 ] [0,\frac{b}{d}-1] [0,db1]上有唯一解

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 ba,速度差为 m − n m-n mn

求解 ( m − n ) x − y l = b − a (m-n)x-yl = b-a (mn)xyl=ba

#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) \\ Lx8L8x1L98x9L98(10x1)dgcd(8,L)d9L10x1 10x1(mod d9L)xgcd(10,d9L)=1d9L=C 10φ(C)1(mod C)φ(C)xxφ(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)10x1(mod C)10qr1(mod C)10r1(mod C)xx φ(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 \\ xa1(mod m1)xa2(mod m2)xan(mod mn)(m1mn)M=m1m2mnMi=miMtiMimitiMi1(mod mi)x=i=1naiMiti

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[n1]+b[f2]+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=1ik1f(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) (ni+1)(mj+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(ni+1)(mj+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

image-20210812205717130

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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+mnCn+mm1

#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=54sum

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]=Sum13af[a+1][b][c][d][x][y]+Sum13bf[a][b+1][c][d][x][y]+Sum13cf[a][b][c+1][d][x][y]+Sum13df[a][b][c][d+1][x][y]+0i3minf[a][b][c][d][i][y](x==4)+0j3minf[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+ (1x)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++=1x21

0个或一个: 1 + x = 1 − x 2 1 − x 1+x=\frac{1-x^2}{1-x} 1+x=1x1x2

0个,1个或两个: 1 + x + x 2 = 1 − x 3 1 − x 1+x+x^2=\frac{1-x^3}{1-x} 1+x+x2=1x1x3

奇数个: x + x 3 + ⋯ + = x 1 − x 2 x+x^3+\dots+=\frac{x}{1-x^2} x+x3++=1x2x

4的倍数个: 1 + x 4 + x 8 + ⋯ + = 1 1 − x 4 1+x^4+x^8+\dots+=\frac{1}{1-x^4} 1+x4+x8++=1x41

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=1x1x4

不超过一个: 1 + x = 1 − x 2 1 − x 1+x=\frac{1-x^2}{1-x} 1+x=1x1x2

3的倍数个: 1 + x 3 + x 6 + ⋯ + = 1 1 − x 3 1+x^3+x^6+\dots+ =\frac{1}{1-x^3} 1+x3+x6++=1x31

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)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值