zoukankan      html  css  js  c++  java
  • Day1

    开个博客,一来作为总结,二来提醒自己还有很多内容需要不断完善。

    A - 整数分解为2的幂(51Nod 1383)

    课上学习了聚聚的代码,以及划分子问题的思想。
    考虑数 n = 1+2+4+……,即1和其他2次幂,其他2次幂提取出2,又变为子问题求解。

    #include <cstdio>
    const int maxn = 1e6+10;
    #define LL long long
    const LL mod = 1e9+7;
    LL a[maxn];
    LL dfs(int n){
        if(n <= 1) return 1;
        if(a[n]) return a[n];
        if(n & 1){
            return a[n] = dfs(n-1)%mod;
        }else{
            return a[n] = (dfs(n-1)%mod + dfs(n/2)%mod) % mod;
        }
    }
    int main(){
        int n;
        while(scanf("%d", &n) != EOF){
            printf("%lld\n", dfs(n));
        }
        return 0;
    }
    

    B - Permutations(poj 2369)

    Thinking:
    看完题目,可以想到是置换群的知识。
    置换:有限集合S到自身的一个一一映射叫做一个置换,即n个元的一种排列变为另一种排列。
    题目里的Pn(k)可以看作置换的乘积。
    题目思路是求置换中循环节长度,再求其最小公倍数。
    最小公倍数求法是a*b/gcd(a,b)。
    循环节长度此处用dfs()遍历求得。

    #include <cstdio>
    bool vis[1005];
    int a[1005];
    int num;
    //求每个循环节的长度
    void dfs(int n){
        if(vis[n])    return;
        vis[n] = true;
        num++;
        dfs(a[n]);
    }
    int gcd(int a, int b){
        return b ?  gcd(b, a%b) : a;
    }
    int lcm(int a, int b){  //最小公倍数
        return a/gcd(a, b)*b;
    }
    int main(){
        int n;
        scanf("%d",&n);
        for(int i=1; i<=n; i++){ scanf("%d",&a[i]); }
        int k = 1;
        for(int i=1; i<=n; i++){
            if(!vis[i]){
                num=0;
                dfs(i);
                k = lcm(k, num);
            }
        }
        printf("%d",k);
        return 0;
    }
    

    C - Invoker

    Thinking:
    最开始对题意有了错误理解,(错误想法原因是看不懂题目所以瞎猜样例),错误思路:利用容斥原理,分An种情况,即Ai代表用i(1<=i<=m)种元素组成n个元素的技能,然后思考消去重复部分,然后就没有然后了。

    下来我参考了ppt的课后提示,是标准的polya定理题。(这个定理现在还不是很懂)
    参考了ppt中旋转和翻折置换的公式,不等价方案数的性质等概念还很模糊。
    参考别人的代码勉强过了这题。
    还有就是关于求组合数的两种方法:1、打表。 2、逆元求组合数。
    还有就是关于逆元的求法: 1、扩展欧几里得算法。 2、费马小定理。
    这个参考博客

    #include <cstdio>
    #define LL long long
    const LL maxn(10010), mod(1e9+7);
    
    LL pow(LL a, LL n, LL p){
        LL ans = 1;
        while(n){
            if(n&1)    ans = ans * a % p;
            a = a * a % p;
            n >>= 1;
        }
        return ans;
    }
    LL niyuan(LL a, LL b){
        return pow(a, b-2, b);
    }
    LL gcd(LL a, LL b){
        return b==0 ? a : gcd(b, a%b);
    }
    LL polya(LL m, LL n){
        LL i, ans=0;
        //旋转
        for(i=1; i<=n; i++){
            ans = (ans + pow(m, gcd(i,n),mod))%mod;
        }
        //翻转
        if(n&1){
            ans = (ans + n*pow(m, (n+1)/2, mod))%mod;
        }else{
            ans = (ans + n/2*(pow(m,n/2+1, mod) + pow(m, n/2, mod))%mod)%mod;
        }
        ans = ans%mod*niyuan(2*n,mod)%mod;
        return ans;
    }
    
    int main(){
        int cas;
        scanf("%d",&cas);
        for(int ca=1; ca<=cas; ca++){
            int n,m;
            scanf("%d%d",&m, &n);
            printf("Case #%d: %lld\n",ca, polya(m,n));
        }
        return 0;
    }
    

    update(2018-08-31)

    Burnside定理:如果某一个方案s经过一个置换f后不变,则称s为f的不动点,将f的所有不动点数目记为c(f),那么等价类数目就等于所有c(f)的平均值。

    polya定理:如果将置换f分解成m(f)个循环的乘积,每个循环内所有格子的颜色必须相同。并假设涂k种颜色的话,那么c(f)等于k^m(f)。 得: 等价类的个数就是所有置换的k^m(f)的平均值。

    本题是等价类计数问题,有两种置换:旋转和翻折。训练指南147页。

    D - Aeroplane chess

    想起课上那个饮料问题,求平均次数即求期望。与此题相似,找递推公式。
    状态定义:dp[i]为扔筛子期望数。
    看ppt提示说逆推,想了想,发现确实如此,因为正推时有多个起点且概率不等,枚举时无法计算。
    逆推时分两种情况:1、飞行点和起点以及终点不用投筛子。 2、其他点则枚举筛子6个等概率点。
    再扔一次筛子期望加一,因为枚举计算的是下一个状态的期望,当前状态则需要多仍一次才可以。

    #include <cstdio>
    #include <cstring>
    const int N(100010);
    int flight[N];
    double dp[N];
    int main(){
        int n,m;
        while( scanf("%d%d", &n, &m) && (n||m) ){
            memset(flight, -1, sizeof(flight));
            for(int i=0; i<m; i++){
                int x,y;
                scanf("%d%d",&x, &y);
                flight[x] = y;
            }
            memset(dp, 0, sizeof(dp));
            for(int i=n-1; i>=0; i--){
                if(flight[i] != -1){
                    dp[i] = dp[flight[i]];
                }else{
                    dp[i] = 1;
                    for(int j=i+1; j<=i+6; j++){
                        dp[i] += 1.0/6*dp[j];
                    }
                }
            }
            printf("%.4lf\n", dp[0]);
        }
        return 0;
    }
    

    后面还有很多题,奈何水平有限,所以慢慢补吧。

  • 相关阅读:
    PHP对象
    MySQL多表更新
    使用not in的子查询
    MySQL比较运算符的子查询
    控制器调用函数
    MVC目录规范
    MVC流程
    mxnet安装
    离线安装Python包hickle,easydict
    深度学习基础
  • 原文地址:https://www.cnblogs.com/seaupnice/p/9404652.html
Copyright © 2011-2022 走看看