加载中...

博弈论 + 组合数

NIM博弈
对于先手:永远将奇数台阶保持不变 我永远看到奇数层不一致的 对手看到永远是 一致的
先手必胜需要每个值异或不等0 这样留给后手操作的就是每个值异或等于0 操作完不为0的情况

台阶-NIM
对于先手:永远将奇数台阶保持不变 我永远看到奇数层不一致的 对手看到永远是 一致的(为输的局面)
奇数台阶石子数量是0 先手必败 不是0 先手必胜
对手从偶数层台阶 拿多少石子到奇数层台阶 我就把哪些石子拿到下层保持奇数级台阶不变

max函数 找到可以达到的情况没有的最小自然数
sg函数 sg(终点)=0 为无法操作的情况(无出边) sg()!=0 那么下一条路粗存在一种情况让sg()=0即让对手到达这个终点的情况

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;

const int N = 110,M=1e4+10;//M表示石子真实个数的值sg代表的值 n为可以进行的操作
int n,m;
int f[M],s[N];
int sg(int x){
    if(f[x]!=-1) {
            return f[x];        
    }
    unordered_set<int>S;//S存出度结点的sg值
    for (int i = 0; i < m; i ++ ){//遍历每种可能的操作
        int sum=s[i];
        if(x>=sum) S.insert(sg(x-sum));//如果可以操作 递归下一个值
        
    }
    for (int i = 0; ; i ++ ){//找max操作
        if(!S.count(i))
        return f[x]=i;
    }
    //最后一个renturn
    
}
int main()
{
    cin >> m;
    for (int i = 0; i < m; i ++ ) cin >> s[i];//s存每种操作的集合
    cin >> n;
    memset(f, -1, sizeof f);
    int res=0;
    
    for (int i = 0; i < n; i ++ ){
        int x;
        cin >> x;
        res^=sg(x);//处理每个初始的情况 对多个图
    }
    //0为胜
    if(res) cout << "Yes";//有向图任何一个非0的都会有情况会到0,任何一个0的情况到不了0
    else cout << "No";
    return 0;
}

sg(xy)=sg(x)sg(y);

#include <iostream>
#include <cstring>
#include <unordered_set>

using namespace std;

const int N = 110;

int n;
int f[N];
unordered_set<int> S;

int sg(int x)
{
    if(f[x] != -1) return f[x];

    for(int i = 0 ; i < x ; i++)
        for(int j = 0 ; j <= i ; j++)//规定j不大于i,避免重复
            S.insert(sg(i) ^ sg(j));//存入下一条边 相当于一个局面拆分成了两个局面,由SG函数理论,多个独立局面的SG值,
                                   //等于这些局面SG值的异或和

    for(int i = 0 ; ; i++)
        if(!S.count(i))
            return f[x] = i;
}

int main()
{
    memset(f , -1 , sizeof f);

    cin >> n;
    int res = 0;
    while(n--)
    {
        int x;
        cin >> x;
        res ^= sg(x);
    }

    if(res) puts("Yes");
    else puts("No");
    return 0;
}



组合数 1≤b≤a≤2000

c[i][j]=c[i-1][j]+c[i-1][j-1]
分成两个情况 选择这个果 在i-1的果里面选择j-1个 不选这果 在i-1里面选择剩下j个果

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2005,mod=1e9+7;
int c[N][N];
void init(){
    for (int i = 0; i < N; i ++ ){//这里写成等于N会报爆se
        for (int j = 0; j <= i; j ++ ){//i表示C右下面 j表示C右上角
            
            if(!j) c[i][j]=1;
            else 
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;            
        }
    }
}
int main()
{
    int n;cin>>n;init();
    while (n -- ){
        int a,b;cin>>a>>b;
        cout << c[a][b]<<endl;
    }
    return 0;
}

组合数二 1≤b≤a≤1e5
使用逆元 计算 (a!/((a-b)!b!)) %mod=a!(a-b!逆元)%mod*(b!逆元)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
long long  jie[N],injie[N];
const int mod = 1e9+7;
int qmi(long long  a,int k){
    int res=1;
    while (k  ){
        if(k&1) res=res*a%mod;
        a=a*a%mod;
        k>>=1;
    }
    return res;
    
}
int main()
{
    
    
    jie[0]=1;
    injie[0]=1;
    for (int i = 1; i < N; i ++ ){
        jie[i]=jie[i-1]*i%mod;
        injie[i]=(long long)injie[i-1]*qmi( i,mod-2 ) %mod;
    }
    int n;
    cin >>n;
    while (n -- ){
        int a,b;cin>>a>>b;
        cout <<(LL)jie[a]*injie[b]%mod*injie[a-b]%mod <<endl;//记录要开个LL
    }
    
    return 0;
}

求组合数3 1≤b≤a≤1e18

lucas定理 lucas(a,b) = (LL)c(a%p,b%p)*lucas(a/p,b/p)%p;

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int p;
int qmi(LL a,int k){
    int res=1;
    while (k ){
        if(k&1) res=(LL)res*a%p;
        a=a*a%p;
        k>>=1;
        
    }
    return res;
}
LL c(LL a,LL b){
    if(b>a) return 0;
    int res=1;
    for (int i = 1,j=a; i <= b; i ++,j-- ){
        res=(LL)res*j%p;
        res=(LL)res*qmi(i,p-2)%p;
    }
    return res;
}
int lucas(LL a,LL b){//这里一定要LL
    if(a<p&&b<p) return c( a,b );
    return (LL)c(a%p,b%p)*lucas(a/p,b/p)%p;
}
int main()
{
    int n ;cin>>n;
    LL a,b;
    while (n -- ){
        cin >> a>>b>>p;
        cout << lucas(a,b)<<endl;
  
    }
    return 0;
}

求组合数4 不能模一个数 需要高精度


posted @ 2022-03-01 19:14  liang302  阅读(34)  评论(0编辑  收藏  举报