C ++テンプレートの概要ペンの質問(D)

1、素数

//1、试除法
bool is_prime(int n)
{
    if (n < 2) return false;
    for (int i = 2; i <= n; i ++)
        if (n % i == 0)
            return false;
    return true;
}
//2、试除法优化
/*如果d/n能整除,那么n/d / n也能整除。比如n = 12, 3和4都是他的约数,n的约数都是成对出现的  。在枚举的时候我们可以只枚举没对较小的一个就可以了。
*/
bool is_prime(int n)
{
    if (n < 2) return false;
    //i <= n / i 时间复杂度根号n
    for (int i = 2; i <= n / i; i ++)
        if (n % i == 0)
            return false;
    return true;
}

2、素因数分解

bool divide(int n)
{
    //i <= n / i 时间复杂度根号n
    for (int i = 2; i <= n / i; i ++)
        if (n % i == 0)//i一定是质数
        {
            int s = 0;
            while(n % i == 0)
            {
                n /= i;
                s ++;
            }
            printf("%d %d\n", i, s);//对于每个正整数ai,按照从小到大的顺序输出其分解质因数后,每个质因数的底数和指数,每个底数和指数占一行。
        }
    //每个质因数的底数和指数,每个底数和指数占一行
    if (n > 1) printf("%d %d\n", n, 1);
    puts("");
}

3、画面素数

1〜N N / LN(n)を素数と素数であり、すべての複数の番号を削除するためには、最後に残った素数です。

int primes[N], cnt;//primes[]存储所有素数
bool st[N];//st[x]存储x是否被筛掉
//朴素做法
void get_primes(int n)
{
    for (int i = 2; i <= n; i ++){//从2开始枚举
        
        if (!st[i])primes[cnt ++ ] = n;//当前数没有被筛过,则是个质数
        for (int j = i + i; j <= n; j +=i) //j = i + i,将n的倍数删除掉
            st[j] = true;//标记为true
    }
}
 
//埃式筛法-O(nloglogn)
void get_primes(int n) {
    for(int i = 2; i <= n; i++) {
        if(!st[i]){ 
            prime[cnt++] = i;
            for(int j = i; j <= n; j += i)
                st[j] = true;
        }
    } 
}
 
//线性筛法-O(n), n = 1e7的时候基本就比埃式筛法快一倍了
//算法核心:x仅会被其最小质因子筛去
void get_prime(int x) {
    for(int i = 2; i <= x; i++) {
        if(!st[i]) prime[cnt++] = i;//找到质数
        for(int j = 0; prime[j] <= x / i; j++) {//从0开始找j倍数
            //对于任意一个合数x,假设pj为x最小质因子,当i<x/pj时,一定会被筛掉
            st[prime[j]*i] = true;//倍数直接标记
            if(i % prime[j] == 0) break;
            /*
            1.i%pj == 0, pj定为i最小质因子,pj也定为pj*i最小质因子
            2.i%pj != 0, pj定小于i的所有质因子,所以pj也为pj*i最小质因子
            */
        }
    }
} 

シーク試験分割数約4、

1、小から大までの数についてのすべてを列挙します

のみについて、いくつかのマイナーのそれぞれを列挙する2、

私!= N / iは、リストに追加された場合は3、

#include <iostream>
#include <algorithm>
#include <vector>//存一个数所有约数
 
using namespace std;
 
vector<int> get_divisors(int n)
{
    vector<int> res;
    
    for (int i = 1; i <= n / i; i ++)//循环遍历找约数
        if (n % i == 0)//如果是一个约数,则存起来
        {
            res.push_back(i);
            if (i != n / i) res.push_back(n / i);//可能会出现n = i*i,两个余数相同的情况,所以需要判断一下
        }
    //最后将约数排个序
    sort(res.begin(), res.end());
    return res;
}
 
int main()
{
    int n;
    cin >> n;
    
    while (n --)
    {
        int x;
        cin >> x;
        auto res = get_divisors(x);//dedaox所有的约数
        //循环遍历输出x的约数
        for (auto t : res) cout << t << ' ';
        cout << endl;
    }
    return 0;
}

5、及び数について

実際に全ての数は、もしN = P1 ^ C1 * P2 ^、Nの約数に適用 C2 * ... * PK ^のCK
(C1 + 1)*(:除数番号 C2 + 1)*。 .. *(CK + 1)
の数についてと:(P1 ^ 0 + P1 ^ 1 + ... + P1 ^ C1)* ... *(PK ^ 0 + PK ^ 1 + ... + PK ^ CK)

#include <iostream>
#include <algorithm>
#include <unordered_map>
 
using namespace std;
 
typedef long long LL;
 
const int mod = 1e9 + 7;
 
int main()
{
    int n;
    cin >> n;
    
    //定义哈希表存所有底数和指数
    unordered_map<int, int> primes;
    
    while (n --)
    {
        int x;
        cin >> x;
        //遍历求x质因数
        for (int i = 2; i <= x / i; i ++)
            while (x % i == 0)
            {
                x /= i;
                //i的质因数指数+1
                primes[i] ++ ;//存质因数指数
            }
        //判断如果x>1说明x是一个比较大的质因数
        if (x > 1) primes[x] ++;//将剩下的质因数加上就好了
    }
    
    //枚举所有的质因数
    LL res = 1;
    //带入求和公式:
    for (auto prime : primes) 
    {
        //1、枚举每一个质数
        int p = prime.first, a = prime.second;//p表示质数的底数,a表示质数的底数
        LL t = 1;//t来存每一部分的总和
        //2、先求P的0次方+...+P的a次方,然后每次对t进行操作: t = p * t + 1
        //t = 1,p + 1;第二次t = 2,p^2 + p + 1;第a次p^a +...+1
        while (a --) t = (t * p + 1) % mod;
        res = res * t % mod;
    }
    cout << res << endl;//得到结果
    
    return 0;
}

6、最大公約数(ユークリッド)

次に、分割D、D割り切れるB、+ Bによるその後D割り切れるは、(AX +によって)分割することができる場合(a、b)は最大公約数=(B、MOD B) 。

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;//b如果不是0返回a%b最大公约数,如果为0则返回a
}

図7に示すように、オイラーの関数

オイラー関数ふるいです

数が素数である場合は1、、1〜P-1素数であります

2、I%の素数[J] == 0、iは素因数の説明P [J]である場合です。

3、もし私%の素数[J]!= 0、PJは、次に、(I * PJ)最低品質係数です。

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int n;
    cin >> n;
    
    while (n --)
    {
        int a;
        cin >> a;
        int res = a;//答案用res表示
        //分解质因数
        for (int i = 2; i <= a / i; i ++)
            if (a % i == 0)
            {
                res = res / i * (i - 1);//等价于i*(1-1/i)
                while (a % i == 0) a /= i; 
            }
        if (a > 1) res = res / a*(a - 1);//如果还没除完,再除一次
        
        cout << res << endl;    
    }
    
    return 0;
}

//筛选法求欧拉函数

//primes存的每一个质数, cnt存质数下标
int primes[N], cnt;
//欧拉函数
int phi[N];
bool st[N];//表示每个数是否被筛到
 
LL get_eulers(int n)
{
    phi[1] = 1;//定义第一个
    for (int i = 2; i <= n; i ++)
    {
        if (!st[i])//如果没被筛过
        {
            primes[cnt ++] = i;
            phi[i] = i - 1;//如果这个数是质数,1~p-1都是质数
        }
        //从小到大枚举所有质数
        for (int j = 0; primes[j] <= n / i; j ++)
        {
            st[primes[j] * i] = true;//标记已经筛过
            if (i % primes[j] == 0) //break;//一个优化变成线性
            {
                //i % primes[j] == 0,说明i是p[j]的质因子
                phi[primes[j] * i] = phi[i] * primes[j];
                break;
            }
            //如果i % primes[j] != 0,pj是(i*pj)的最小质因子
            phi[primes[j] * i] = phi[i] * (primes[j] - 1);
        }
    }
    
    LL res = 0;//定义总和
    for (int i = 1; i <= n; i ++) res += phi[i];//求总和
    return res;
}

8、高速電力

^ k個のmod p個の結果を高速見つけます

int qmi(int a, int k, int p)
{
    int res = 1;//答案
    while (k)
    {
        //第一次迭代从a^0开始,1开始
        if (k & 1) res = (LL) res * a % p;//如果当前k的末位是1,则是第一次迭代
        //把k的末位删除
        k >>= 1;
        //把a变成下一个
        a = (LL)a * a % p;//a平方一下
        
    }
    return res;//返回res
}

//Example-求逆元
#include <iostream>
#include <algorithm>
 
using namespace std;
 
typedef long long LL;
 
//a^k % p
int qmi(int a, int k, int p)
{
    int res = 1;//答案
    while (k)
    {
        //第一次迭代从a^0开始,1开始
        if (k & 1) res = (LL) res * a % p;//如果当前k的末位是1,则是第一次迭代
        //把k的末位删除
        k >>= 1;
        //把a变成下一个
        a = (LL)a * a % p;//a平方一下
        
    }
    return res;//返回res
}
 
int main()
{
    int n;
    //读入数据比较多yong scanf
    scanf("%d", &n);
    while (n --)
    {
        int a, p;
        scanf ("%d%d", &a, &p);
        
        int res = qmi(a, p - 2, p);//费马定理,求出结果
        if (a % p) printf("%d\n", res);//说明d不是p的倍数,输出res
        //如果p = 2比较特殊,是0次方,需要特判
        else printf("impossible\n");//d是p的倍数说明一定无解
    }
    
    return 0;
}

図9に示すように、拡張ユークリッドアルゴリズム - 線形合同式

概念:考えると、B、mは、整数xを見つけるように:AX = B(MOD M)。

例:2X = 3(6 MOD)は溶液ありません

          4X = 3(MOD 5):4 * / 5の数の残りから多数の異なる4 *は、3に等しくてもよく、Xは、2,7等です。

変形式:

AX = B(MOD m)は、Y、Zの整数部の存在に相当するようなものであるAX = Mという* Y + B - > AX - 私= B - > y` = Y、AX + my` = B。

注:この式は、bが(メートル)で割り切れることを確立することができる最大公約数、(M)| B、次いでこの溶液があり、それ以外の場合はノーソリューションよ。
 

int exgcd(int a, int b, int &x, int &y)
{
    //return b ? gcd(b, b % a) : a;
    if (!b) //如果b = 0,ax + by = a
    {
        //得到一组解
        x = 1, y = 0;
        return a;//如果b = 0,返回a
    }
    //by + (a mod b)x = (a, b)最大公约数d
    int d = exgcd(b, a % b, y, x);//存下最大公约数,翻转了一下
    //a mod b = a - (a / b)下取整 * b带入
    //by + (a - (a / b)下取整 * b)x = d;
    //展开整理:ax + b (y - (a/b)*x)
    //得到a的系数不变为a,y的系数变成:y - (a/b)*x
    y -= a / b * x;
    
    return d;
}

10、ガウスの消去法

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
 
const int N = 110;
const double eps = 1e-6;
int n;
double a[N][N];//存矩阵
 
int gauss()
{
    int c, r;//定义行和列
    //1、从第0行第0列开始,找到绝对值最大的一行
    for (c= 0, r = 0; c < n; c ++)
    {
        int t = r;
        for (int i = r; i < n; i ++)
            //如果找到绝对值比较大的一列,fabs表示绝对值函数
            if (fabs(a[i][c] > fabs(a[t][c])))
                t = i;//换成当前列
        //因为值是double类型,所以为了防止出现精度问题我们需要特判一下
        if (fabs(a[t][c]) < eps) continue;//fabs是返回浮点数绝对值,如果当前是0,返回continue
        
        //2、把当前绝对值最大一行换到最上面去
        for (int i= c; i <= n; i ++) swap(a[t][i], a[r][i]);
        //3、将换上去的那行第一个数化成1
        //从最后一个开始换
        for (int i = n; i >= c; i --) a[r][i] /= a[r][c];
        //4、将下面所有行的当前列,消成0
        for (int i = r + 1; i < n; i ++)
            if (fabs(a[i][c]) > eps)//从第二列开始,如果第一个数大于零才进行消0操作
                for (int j = n; j >= c; j --)//把第一行通过乘除操作,是的第一行第一个数与第二行第一个数相同,然后进行相减
                    a[i][j] -= a[r][j] * a[i][c];//a[r][j] * a[i][c]:第一行的数*第i行第一个数的值
        //操作完记得 r ++
        r ++;
    }
    if (r < n)//5、结果不到n个,可能存在无解或者无穷多个解
    {
        //如果出现0 = 非零情况,则无解
        for (int i = r; i < n; i ++) 
            if (fabs(a[i][n]) > eps)//每行最后一个存在>0的结果,即0 = 非零情况,则无解
                return 2;//无解
        return 1;//否则有无穷多种解
    }
    //6、找到三角结构,就互相消元。倒着把方程消一遍,把除了第一个数以外后面的数消成0
    for (int i = n - 1; i >= 0; i --)
        for (int j = i + 1; j < n; j ++)
            //从第最后一个i个开始消
            //把当前这方程除了xi这个系数之外所有系数消成0,减去他的系数*他的值
            a[i][n] -= a[i][j] * a[j][n];//a[j][n]是xj的值
    
    
    return 0;//如果r = n,则只有唯一解
}
 
int main()
{
    cin >> n;//n行n列
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n + 1; j ++)
            cin >> a[i][j];//构建输入矩阵
    
    //高斯消元
    int t = gauss();
    //得到三种解:唯一解、无穷多种解、无解
    if (t == 0)//t = 0唯一解
    {
        for (int i = 0; i < n; i ++) printf("%.2f\n", a[i][n]);
    }//t = 1无穷多种解
    else if (t == 1) puts("Infinite group solutions");
    //t = 2无解 
    else puts("No solution");
    
    return 0;
}

11、組み合わせの数を見つけます

//递归法求组合数
// c[a][b] 表示从a个苹果中选b个的方案数
for (int i = 0; i < N; i ++ )
    for (int j = 0; j <= i; j ++ )
        if (!j) c[i][j] = 1;//如果是第一个数0,则标记1
        else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;////按照公式来

//
/*首先预处理出所有阶乘取模的余数fact[N],以及所有阶乘取模的逆元infact[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;
}

// 预处理阶乘的余数和阶乘逆元的余数
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ )
{
    fact[i] = (LL)fact[i - 1] * i % mod;
    infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}

12、ゲーム理論

定理:場合にのみ、A1がA2 ^ ^ ... ^アン=場合NIM上部の手は、ゲームに勝つために 0!

方法:奇数ステップのXOR場合、奇数の手順を見つけて下さい!= 0、そして勝利。

十分条件:すべては、すべての奇数アップ石ステップXOR 0に等しいされていません。

#include <iostream>
#include <algorithm>
 
using namespace std;
 
int main()
{
    int n;
    int res = 0;
    
    scanf ("%d", &n);
    for (int i = 1; i <= n; i ++)
    {
        int x;
        scanf ("%d", &x);
        if (i % 2) res ^= x;//所有奇数级台阶所有石子异或起来不等于0.
    }
    
    if (res) puts("Yes");
    else puts("No");
    
    return 0;
}

メックス操作
集合Sは非負の整数を表しています。存在しない自然数の集合のうち、最小の検索:定義MEX(S)は、すなわち、最小の非負整数演算の集合Sに属さないと判断されます。
MEX(S)=分{X }、xは自然数であり、x Sに属しません

SG機能

合計図からゲームを開始するために設けられた各ノードxのkは、存在する有向エッジをX、各ノードは、Y1に達し、Y2、...、YK、定義されているSG(X)は、後継ノードX Y1、Y2は... 、その後、構成セットSG関数値YKは、MEX(S)の計算結果、すなわち、実行:
SG(X)= MEX({SG(Y1)、SG(Y2)、···、SG(YK)})を
、特に、 SG関数値全体有向グラフGは次のように定義され、図ゲーム関数値SG、すなわち、SG(G)= SG(Sへゲーム開始点S )。SG(終了)= 0、Y1、Y2、...、YN状況があります。エンドポイントが最小の自然数と比較して、フロントエンドポイントが存在しない場合、0として定義され、すなわち、図最後の点(第2経路)、数は0と1の他、次の最小の自然数を存在しない、それは再帰ので、2であります、最低の自然数が存在しないことを最初は3です。

結論:SG = 0、失敗する運命に、SG!= 0は失敗する運命に。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
 
using namespace std;
 
const int N = 110, M = 10010;
 
int n, m;
int s[N], f[M];//s表示谁的个数,f表示SG的值
 
//求sg
int sg(int x)
{
    //记忆化搜索--优化
    if (f[x] != -1) return f[x];//如果每个状态都被算过直接返回结果
    
    //哈希表存他所有可以到的局面
    unordered_set<int> S;
    for (int i = 0; i < m; i ++)
    {
        //当前数的个数
        int sum = s[i];
        //如果当前个数>=sum,才可以取这些数
        if (x >= sum) S.insert(sg(x - sum));
    }
    //判断当前集合当中不存在的点
    for (int i = 0; ; i ++)
        if (!S.count(i))//如果当前数不存在,当前的值就是i
            return f[x] = i;
}
 
int main()
{
    cin >> m;//读入m个数
    //输入操作
    for (int i = 0; i < m; i ++) cin >> s[i];
    cin >> n;//读入n,求的SG,每堆的个数
    
    memset(f, -1, sizeof f);//用记忆化搜索
    
    //求每一堆的个数
    int res = 0;
    for (int i = 0; i < n; i ++)
    {
        int x;
        cin >> x;
        res ^= sg(x);
    }
    
    if (res) puts("Yes");
    else puts("No");
    
    return 0;
}

 

 

 

 

 

公開された144元の記事 ウォン称賛17 ビュー20000 +

おすすめ

転載: blog.csdn.net/qq_27262727/article/details/104789231