zoukankan      html  css  js  c++  java
  • 2016 Multi-University Training Contest 6

    5/12

    2016 Multi-University Training Contest 6

    官方题解

    打表找规律/推公式 A A Boring Question(BH)

    题意:

      ,意思就是在[0,n]里选择m个数字的相邻数字二项式组合的积的总和。

    思路:

    想了好久,不会,但是这题有300多人过,怀疑人生。。。

    打了个表:

    n=0, m=2, ans=1
    n=1, m=2, ans=3
    n=2, m=2, ans=7
    n=3, m=2, ans=15
    n=4, m=2, ans=31
    n=5, m=2, ans=63
    n=6, m=2, ans=127
    n=7, m=2, ans=900198674
    n=8, m=2, ans=1590575918
    n=0, m=3, ans=1
    n=1, m=3, ans=4
    n=2, m=3, ans=13
    n=3, m=3, ans=40
    n=4, m=3, ans=121
    n=5, m=3, ans=364
    n=6, m=3, ans=1093
    n=7, m=3, ans=-457914394
    n=8, m=3, ans=-624508303
    n=0, m=4, ans=1
    n=1, m=4, ans=5
    n=2, m=4, ans=21
    n=3, m=4, ans=85
    n=4, m=4, ans=341
    n=5, m=4, ans=1365
    n=6, m=4, ans=5461
    n=7, m=4, ans=-914025821
    n=8, m=4, ans=-1903277640
    n=0, m=5, ans=1
    n=1, m=5, ans=6
    n=2, m=5, ans=31
    n=3, m=5, ans=156
    n=4, m=5, ans=781
    n=5, m=5, ans=3906
    n=6, m=5, ans=19531
    n=7, m=5, ans=-681221710
    n=8, m=5, ans=1872878440
    n=0, m=6, ans=1
    n=1, m=6, ans=7
    n=2, m=6, ans=43
    n=3, m=6, ans=259
    n=4, m=6, ans=1555
    n=5, m=6, ans=9331
    n=6, m=6, ans=55987
    n=7, m=6, ans=-199384196
    n=8, m=6, ans=638696943

    按照m排序就能看出规律,想到比赛快结束的时候,最后没时间交题了,好气啊。

    官方解答:

    代码:

    #include <bits/stdc++.h>
    
    typedef unsigned long long ll;
    const int N = 1e5 + 5;
    const int MOD = 1000000007;
    int fact[N];
    
    void init_fact(int n) {
        fact[0] = 1;
        for (int i=1; i<=n; ++i) {
            fact[i] = (ll) fact[i-1] * i % MOD;
        }
    }
    
    int pow_mod(int x, int n, int MOD) {
        int ret = 1;
        for (; n; n>>=1) {
            if (n & 1) ret = (ll) ret * x % MOD;
            x = (ll) x * x % MOD;
        }
        return ret;
    }
    
    int Inv(int x) {
        return pow_mod (x, MOD - 2, MOD);
    }
    
    int n, m;
    int tot;
    int k[N], b[N];
    
    int calc() {
        int ret = 0;
        int tmp = 1;
        for (int i=2; i<=m; ++i) {
            tmp = (ll) tmp * fact[b[i]] % MOD;
        }
        ret = (ll) fact[k[m]] * Inv (fact[k[1]]) % MOD * Inv (tmp) % MOD;
        return ret;
    }
    
    void DFS(int cur, int len, int &ans) {
        if (len == m + 1) {
            ans += calc ();
            return ;
        }
        for (int i=0; i<=n; ++i) {
            k[len] = i; b[len] = k[len] - k[len-1];
            DFS (i, len+1, ans);
        }
    }
    
    int brute(int n, int m) {
        int ret = 0;
        for (int i=0; i<=n; ++i) {
            k[1] = i;
            DFS (i, 2, ret);
        }
        return ret;
    }
    
    int solve() {
        if (n == 0) return 1;
        return (1 + (ll) m * (pow_mod (m, n, MOD) - 1 + MOD) % MOD * Inv (m - 1)) % MOD;
    }
    
    int main() {
        int T;
        scanf ("%d", &T);
        while (T--) {
            scanf ("%d%d", &n, &m);
            //printf ("%d
    ", brute (n, m));
            printf ("%d
    ", solve ());
        }
        return 0;
    }
    

    容斥原理+Lucas定理 B A Simple Chess(BH)

    题意:

      n*m的格子,有r个障碍物,从(1,1)出发不走到障碍物到达(n,m)的方案数。(走法是(x1,y1)->(x1+2,y1+1) or (x1+1,y1+2))

    思路:

      记第一种走法的次数为b次,第二种走法的次数为c次,那么n=1+2c+b,m=1+2b+c。如果不考虑障碍物的话,答案是。那么如果会走到第i个障碍物,那么减去的是从(1,1)到第i个障碍物的位置的方案数(不走到其他的障碍物)乘以从第i个障碍物出发到(n,m)的方案数。注意(n,m)是障碍物的话,方案数直接为0。有了想法后,用代码实现,检验正确性,获得AC,瞬间的快感,这就是ACM的魅力吧。

    代码:

    #include <bits/stdc++.h>
    
    typedef long long ll;
    const int N = 100 + 5;
    const int MOD = 110119;
    
    ll pow_mod(ll x, int n) {
        ll ret = 1;
        for (; n; n>>=1) {
            if (n & 1) ret = ret * x % MOD;
            x = x * x % MOD;
        }
        return ret;
    }
    
    ll Inv(ll x) {
        return pow_mod (x, MOD - 2);
    }
    
    ll fact[MOD];
    
    struct Point {
        ll x, y;
        bool operator < (const Point &rhs) const {
            ll ldis = (x - 1) + (y - 1);
            ll rdis = (rhs.x - 1) + (rhs.y - 1);
            return ldis < rdis;
        }
    }p[N];
    ll res[N];
    ll n, m;
    int r;
    
    void init_fact(int n) {
        fact[0] = 1;
        for (int i=1; i<n; ++i) {
            fact[i] = fact[i-1] * i % MOD;
        }
    }
    
    ll Lucas(ll n, ll k, int p) {
        ll ret = 1;
        while (n && k) {
            ll nn = n % p, kk = k % p;
            if (nn < kk) return 0;
            ret = ret * fact[nn] % p * Inv (fact[kk] * fact[nn-kk] % p) % p;
            n /= p; k /= p;
        }
        return ret;
    }
    
    bool judge_b(ll n, ll m) {
        return (-n + 2 * m - 1) % 3 == 0 && (-n + 2 * m - 1) >= 0;
    }
    
    bool judge_c(ll n, ll m) {
        return (2 * n - m - 1) % 3 == 0 && (2 * n - m - 1) >= 0;
    }
    
    ll get_b(ll n, ll m) {
        return (-n + 2 * m -1) / 3;
    }
    
    ll get_c(ll n, ll m) {
        return (2 * n - m - 1) / 3;
    }
    
    ll solve() {
        //if (r > 0 && p[r-1].x == n && p[r-1].y == m) return 0;
        if (!judge_b (n, m)) return 0;
        if (!judge_c (n, m)) return 0;
        ll b = get_b (n, m);
        ll c = get_c (n, m);
        ll ret = Lucas (b + c, c, MOD);
    
        std::sort (p, p+r);
        memset (res, -1, sizeof (res));
        for (int i=0; i<r; ++i) {
            if (!judge_b (p[i].x, p[i].y)) continue;
            if (!judge_c (p[i].x, p[i].y)) continue;
            if (!judge_b (n-p[i].x+1, m-p[i].y+1)) continue;
            if (!judge_c (n-p[i].x+1, m-p[i].y+1)) continue;
            ll ib = get_b (p[i].x, p[i].y);
            ll ic = get_c (p[i].x, p[i].y);
            res[i] = Lucas (ib+ic, ib, MOD);
    
            for (int j=0; j<i; ++j) {
                if (res[j] == -1) continue;
                if (p[i].x < p[j].x || p[i].y < p[j].y) continue;
                ll nn = p[i].x - p[j].x + 1;
                ll mm = p[i].y - p[j].y + 1;
                if (!judge_b (nn, mm))  continue;
                if (!judge_c (nn, mm)) continue;
                ll jb = get_b (nn, mm);
                ll jc = get_c (nn, mm);
                ll tmp = res[j] * Lucas (jb+jc, jb, MOD) % MOD;
                res[i] = (res[i] - tmp + MOD) % MOD;
            }
            ll nb = get_b (n-p[i].x+1, m-p[i].y+1);
            ll nc = get_c (n-p[i].x+1, m-p[i].y+1);
            ret = (ret - res[i] * Lucas (nb+nc, nb, MOD) % MOD + MOD) % MOD;
        }
        return ret;
    }
    
    int main() {
        init_fact (MOD);
        int cas = 0;
        while (scanf ("%I64d%I64d%d", &n, &m, &r) == 3) {
            bool flag = true;
            for (int i=0; i<r; ++i) {
                scanf ("%I64d%I64d", &p[i].x, &p[i].y);
                if (p[i].x == n && p[i].y == m) flag = false;
            }
            if (!flag) {
                printf ("Case #%d: %I64d
    ", ++cas, 0LL);
                continue;
            }
            printf ("Case #%d: %I64d
    ", ++cas, solve ());
        }
        return 0;
    }
    

    博弈+打表找规律 C A Simple Nim(BH)

    题意:

      除了经典的Nim走法,还多了可以把一堆分成三小堆的走法。

    思路:

      多了一种操作没关系,根据SG定理,只要求出x的所有后继状态的SG函数,SG(x)=mex(S),分成三小堆的状态的SG值看成三个子游戏的Nim和。至于这题的做法,打表找规律即可。

    代码:

    #include <bits/stdc++.h>
    
    int sg[105];
    
    int SG(int n) {
        if (n == 0) return sg[n] = 0;
        if (sg[n] != -1) return sg[n];
        if (n < 3) return sg[n] = n;
        bool vis[1000];
        memset (vis, false, sizeof (vis));
        for (int i=1; i<=n; ++i) {
            for (int j=i; i+j<n; ++j) {
                int k = n - i - j;
                //if (k < i || k < j) continue;
                vis[SG (i) ^ SG (j) ^ SG (k)] = true;
            }
        }
        for (int i=0; i<n; ++i) vis[SG (i)] = true;
        int &ret = sg[n] = 0;
        while (vis[ret]) ret++;
        return ret;
    }
    
    void f() {
        memset (sg, -1, sizeof (sg));
        for (int i=0; i<=100; ++i) {
            printf ("sg[%d]=%d
    ", i, SG (i));
        }
    }
    
    int main() {
        //f ();
        int T;
        scanf ("%d", &T);
        while (T--) {
            int n;
            scanf ("%d", &n);
            long long ans = 0;
            for (int i=0; i<n; ++i) {
                long long x;
                scanf ("%I64d", &x);
                long long sg = x;
                if (x % 8 == 0) sg--;
                if (x % 8 == 7) sg++;
                ans ^= sg;
            }
            puts (ans ? "First player wins." : "Second player wins.");
        }
        return 0;
    }
    

    01背包 H To My Girlfriend(BH)

    题意:

      ,意思是有a[i],a[j],没有a[k],a[l],和为m时的组合数。

    思路:

      想到简单的背包DP,dp[i][j][s1][s2]表示考虑前i个,和为j,且必选了s1个且必不选s2个的方案数。时间复杂度为

    #include <bits/stdc++.h>
    
    const int N = 1e3 + 5;
    const int MOD = 1e9 +7;
    int dp[N][N][3][3];
    int a[N];
    int n, s;
    
    void add_mod(int &a, int b) {
        a += b;
        if (a >= MOD) a -= MOD;
    }
    
    int solve() {
        memset (dp, 0, sizeof (dp));
        dp[0][0][0][0] = 1;
        for (int i=1; i<=n; ++i) {
            for (int j=0; j<=s; ++j) {
                for (int s1=0; s1<=2; ++s1) {
                    for (int s2=0; s2<=2; ++s2) {
                        add_mod (dp[i][j][s1][s2], dp[i-1][j][s1][s2]);  //不选
                        if (j >= a[i]) add_mod (dp[i][j][s1][s2], dp[i-1][j-a[i]][s1][s2]);  //选
                        if (j >= a[i] && s1) add_mod (dp[i][j][s1][s2], dp[i-1][j-a[i]][s1-1][s2]);  //必选
                        if (s2) add_mod (dp[i][j][s1][s2], dp[i-1][j][s1][s2-1]);  //必不选
                    }
                }
            }
        }
        int ret = 0;
        for (int i=1; i<=s; ++i) {
            add_mod (ret, dp[n][i][2][2]);
        }
        return (long long) ret * 4 % MOD;
    }
    
    int main() {
        int T;
        scanf ("%d", &T);
        while (T--) {
            scanf ("%d%d", &n, &s);
            for (int i=1; i<=n; ++i) scanf ("%d", a+i);
            printf ("%d
    ", solve ());
        }
        return 0;
    }

    贪心 J Windows 10(BH)

    题意:

      调音量从p到q,调低的操作,连续的情况下,1,2,4。。。停顿和上升操作都会打断连续,重新从1开始,问最少几次操作。

    思路:

      直观的想法就是拼命的往下降,最后微调(上升或者停顿再下降),考虑到”停顿+一格音量“可以与”上升一格“互换,那么在下降后再上升时考虑能否用停顿替代部分上升,所以要记录停顿的次数,DFS写很好。

    #include <bits/stdc++.h>
    
    typedef long long ll;
    
    ll DFS(ll p, ll q, ll step, ll stop) {
        if (p == q) return step;
        int x = 0;
        while (p - (1<<x) + 1 > q) x++;
        if (p - (1<<x) + 1 == q) return step + x;
        ll up = q - std::max (0LL, (p - (1<<x) + 1));
        ll better = x + std::max (0LL, up - stop);
        return std::min (better + step, DFS (p-(1<<(x-1))+1, q, step+x, stop+1));
    }
    
    int main() {
        int T;
        scanf ("%d", &T);
        while (T--) {
            ll p, q;
            scanf ("%I64d%I64d", &p, &q);
            if (q >= p) {
                printf ("%I64d
    ", q - p);
            } else {
                printf ("%I64d
    ", DFS (p, q, 0, 0));
            }
        }
        return 0;
    }
  • 相关阅读:
    nginx 配置上传文件大小限制
    linux大文件截取某个日期后面的所有内容
    软件架构笔记 五
    软件架构笔记 四
    软甲架构笔记 三
    软件架构笔记 二
    软件架构笔记 一
    c# 生成的没用文件
    c# 两个软件传参
    c# 四则运算出错
  • 原文地址:https://www.cnblogs.com/NEVERSTOPAC/p/5738264.html
Copyright © 2011-2022 走看看