一:扩展欧拉定理的一种更简便的写法

void exgcd(int a,int b,int &d,int &x,int &y){
    if(b) exgcd(b, a % b, d, y, x), y -= x * (a / b);
    else x = 1, y = 0, d = a;
}

例:OJ 1135

二:线性求逆元,若$a*i+b=p,a=\lfloor \frac{p}{i} \rfloor,b=p%i$则$\frac{1}{i}=-\frac{a}{b}$

inv[1] = 1;
for(int i=2; i<=n; i++)
    inv[i] = p - (p / i) * inv[p % i] % p;

 三:大步小步算法,求$a^x=b(mod p)$,令$k=\sqrt{p},0<c<=k,0<=d<k$,原式可转化为$a^{ck-d}=b$,即$a^{ck}=b*a^d$,用hash表保存每一个$b*a^d$,最后依次检验即可

时间复杂度$O(\sqrt{n})$

例题:洛谷 3846

四:牛顿迭代法,令$x_0$为一个任意值,重复令$x_0=x_0-\frac{f(x_0)}{f^{'}(x_0)}$,最后$x_0$会稳定在函数f的零点上,这个方法大部分情况下成立(没有固定的规律)

例:10次循环计算根号(非常快):

int main(){
    while(1){
        double c, x0 = 1;
        scanf("%lf", &c);
        if(!c) break;
        for(int i=1; i<=10; i++)
            x0 = (x0 + c / x0) / 2;
        printf("%.12lf\n", x0);
    }
    return 0;
}

 五:(巨坑)树的计数

1:有标号无根树计数:$Ans = n^{n-2}$,可以考虑prufer序列,序列中的x的数量+1就是图中点的度数,以此来考虑问题

2:有标号有根树计数:$Ans = n*n^{n-2}=n^{n-1}$,就是有标号无根树情况中以每个点为根,所以答案乘以n

3:无标号有根树计数:令$f_k$为k个点的树的数量,转移方程$f_k = \frac{\sum_{i=1}^{k-1} \ \ f_i \sum_{j|k-i} \ \ \ jf_j}{n-1}$

设生成函数$F(x)=x\prod_{i=1}^{\infty}(\frac{1}{1-x^i})^{f_i}$

两边取对数得$lnF(x)=lnx-f_i\sum_{i=1}^{\infty}({1-x^i})$

两边求导得$\frac{F^{'}(x)}{F(x)}=\frac{1}{x}+\sum_{i=1}^{\infty}if_i\frac{x^{i-1}}{1-x^i}$

化简得$xF^{'}(x)=F(x)+F(x)\sum_{i=1}^{\infty}\frac{x^i}{1-x^i}$

对比$x^n$系数得$nf_n=f_n+\sum_{i-1}^{\infty}if_i\sum_{j=ik,k\in N^+}f_{n-j}$

最终化简得到$f_k = \frac{\sum_{i=1}^{k-1} \ \ f_i \sum_{j|k-i} \ \ \ jf_j}{n-1}$

朴素算法时间复杂度是$O(n^2)$,但这里还有一个$O(nlog^2n)$的算法

CDQ分治在这里又排上用场了,设$g(x)==\sum{j|x}jf_j$,则$(n-1)f_n=\sum_{i=1}^{n-1}f_ig_{n-i}$,这不就是卷积的形式吗

先计算$[1,\frac{n}{2}]$的f,之后每计算完成一个f就更新g,因为g(x)只与$j|x的f(j)$有关,所以$\frac{n}{2}$之后的g(x)实际上已经更新完了,所以就可以直接用FFT乘法计算左边对右边的贡献,最后再计算右边之间的贡献就可以了

4:无标号无根树计数:令这个图的重心为根,则它的子树的大小都小于$\frac{n}{2}$,所以答案就是有根树数量减去子树大小大于$\frac{n}{2}$的有根数的数量,注意当n为偶数时要额外减去重心有两个点的情况

例:OJ 1301 1305

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 501, MOD = 998244353;
int a[MAXN], C[MAXN][MAXN], Dp[MAXN][MAXN];

int main(){
    int n = read();
    for(int i=0; i<=n; i++) for(int j=0; j<=i; j++)
        C[i][j] = (j == 0 || j == i) ? 1 : (C[i-1][j-1] + C[i-1][j]) % MOD;
    for(int i=1; i<=n; i++)
        a[i] = read();
    Dp[0][0] = 1;
    for(int i=1; i<=n; i++) for(int j=0; j<=n-2; j++) for(int k=0; k<=min(j,a[i]-1); k++)
        Dp[i][j] = ((long long)C[j][k] * Dp[i-1][j-k] + Dp[i][j]) % MOD;
    printf("%d", Dp[n][n-2]);
    return 0;
}
View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

inline long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

const int MAXN = 2001, MOD = 998244353;
int Dp[MAXN], g[MAXN];

int pow(int a,int x){
    int ans = 1;
    while(x){
        if(x & 1) ans = (long long)ans * a % MOD;
        a = (long long)a * a % MOD, x >>= 1;
    }
    return ans;
}



int main(){
    int n = read();
    Dp[1] = 1, g[1] = 1;
    for(int i=2; i<=n; i++){
        for(int j=1; j<i; j++)
            Dp[i] = ((long long)g[i-j] * Dp[j] + Dp[i]) % MOD;
        Dp[i] = (long long)Dp[i] * pow(i - 1, MOD - 2) % MOD;
        for(int j=1; j<=i; j++) if(i % j == 0)
            g[i] = ((long long)Dp[j] * j + g[i]) % MOD;
    }
    printf("%d", Dp[n]);
    return 0;
}
View Code

 

posted on 2020-03-30 08:38  PHDHD  阅读(144)  评论(0编辑  收藏  举报