2016 Multi-University Training Contest 6

A
顯然有規律可尋

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
#include <cstring>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <set>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
const int INF = 1e9 + 10;
const int MOD = 1e9 + 7;
LL pow_mod(LL a, LL n) {
    LL ans = 1LL;
    while(n) {
        if(n & 1LL) {
            ans = ans * a % MOD;
        }
        a = a * a % MOD;
        n >>= 1LL;
    }
    return ans;
}
int main()
{
    int t; scanf("%d", &t);
    while(t--) {
        LL n, m; scanf("%lld%lld", &n, &m);
        printf("%lld\n", (pow_mod(m, n + 1) - 1 + MOD) % MOD * pow_mod(m - 1, MOD - 2) % MOD);
    }
    return 0;
}

B
題意:每次從(x, y)可以到(x + 2, y + 1),(x + 1, y + 2)。中間有m個格子是壞的,問你從(1, 1) 到 (n, m)的方案數。

dp[i] 表示到達第i個壞格子且不經過其他壞格子的方案數。
dp[i]=i1j=1dp[j]

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
#include <cstring>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <set>
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pii;
const int MAXN = 2e5 + 10;
const int MOD = 110119;
void add(LL &x, LL y) { x += y; if(x < 0) x += MOD; x %= MOD; }
LL fac[MAXN];
void getfac(LL p) {
    fac[0] = 1 % p;
    for(LL i = 1; i <= p; i++) {
        fac[i] = fac[i-1] * i % p;
    }
}
LL pow_mod(LL a, LL n, LL p) {
    LL ans = 1LL;
    while(n) {
        if(n & 1)
            ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}
LL C(LL n, LL m, LL p) {
    if(m > n) return 0;
    else return fac[n] * pow_mod(fac[m] * fac[n-m] % p, p - 2, p) % p;
}
LL Lucas(LL n, LL m, LL p) {
    if(m == 0) {
        return 1 % p;
    }
    else {
        return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
    }
}
LL Solve(LL x1, LL y1, LL x2, LL y2) {
    if(x1 > x2 && y1 > y2) {
        swap(x1, x2); swap(y1, y2);
    }
    if(x1 <= x2 && y1 <= y2) {
        LL d = 2 * y2 - 2 * y1 + x1 - x2;
        if(d >= 0 && d % 3 == 0) {
            LL y = d / 3; d = x2 - y - x1;
            if(d < 0 || d & 1) return 0;
            LL x = d / 2;
            //cout << x << ' ' << y << endl;
            return Lucas(x + 1 + y - 1, x + 1 - 1, MOD);
        }
    }
    return 0;
}
pii a[110];
LL dp[110];
int main()
{
    getfac(MOD);
    LL n, m; int r, kcase = 1;
    while(scanf("%lld%lld%d", &n, &m, &r) != EOF) {
        for(int i = 1; i <= r; i++) {
            scanf("%lld%lld", &a[i].first, &a[i].second);
            dp[i] = 0;
        }
        sort(a + 1, a + r + 1);
        a[r + 1] = pii(n, m); r++; dp[r] = 0;
        for(int i = 1; i <= r; i++) {
            LL sum = 0;
            for(int j = 1; j < i; j++) {
                if(a[i].first >= a[j].first && a[i].second >= a[j].second) {
                    add(sum, dp[j] * Solve(a[j].first, a[j].second, a[i].first, a[i].second) % MOD);
                }
            }
            add(dp[i], Solve(1, 1, a[i].first, a[i].second) - sum);
        }
        printf("Case #%d: %lld\n", kcase++, dp[r]);
    }
    return 0;
}

C
題意:有n堆石子,每次可以選擇從某一個非空堆裏面取出不少於1數目的石子,也可以選擇將該堆分爲三堆石子(不允許爲空)。

SG打表。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
#include <cstring>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <set>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e6 + 10;
const int INF = 1e9 + 10;
const int MOD = 1e9 + 7;
int a[MAXN];
int main()
{
    //freopen("out.txt", "w", stdin);
//    sg[0] = 0, sg[1] = 1, sg[2] = 2;
//    for(int i = 3; i <= 200; i++) {
//        sg[i] = Work(i);
//    }
//    for(int i = 3; i <= 200; i++) {
//        printf("%d %d %d\n", i, sg[i], sg[sg[i]]);
//    }
    int t; scanf("%d", &t);
    while(t--) {
        int n; scanf("%d", &n);
        LL ans = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            if(a[i] % 8 == 0) {
                a[i]--;
            }
            else if(a[i] % 8 == 7) {
                a[i]++;
            }
            ans ^= a[i];
        }
        printf(ans ? "First player wins.\n" : "Second player wins.\n");
    }
    return 0;
}

H
日狗,看了好久不懂題意?
題意:
ni=1nj=1nk=1nl=1sm=1f(i,j,k,l,m)(i,j,k,laredifferent)
意思是讓你找到若干個子集,使得a[i],a[j] 必選,a[k],a[l] 必不選且和<=m
dp[i][j][k][l] 表示前i個數和爲j且k個必選l個必不選的方案數。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
#include <cstring>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <set>
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 2e5 + 10;
const int MOD = 1e9 + 7;
void add(int &x, int y) { x += y; if(x >= MOD) x -= MOD; }
int dp[1001][1001][3][3];
int a[1001];
int main()
{
    int t; scanf("%d", &t);
    while(t--) {
        int n, s; scanf("%d%d", &n, &s);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        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 k = 0; k <= 2; k++) {
                    for(int l = 0; l <= 2; l++) {

                        if(j >= a[i]) {
                            add(dp[i][j][k][l], dp[i - 1][j - a[i]][k][l]);
                            if(k - 1 >= 0) {
                                add(dp[i][j][k][l], dp[i - 1][j - a[i]][k - 1][l]);
                            }
                        }

                        if(l - 1 >= 0) {
                            add(dp[i][j][k][l], dp[i - 1][j][k][l - 1]);
                        }
                        add(dp[i][j][k][l], dp[i - 1][j][k][l]);
                    }
                }
            }
        }
        int ans = 0;
        for(int i = 1; i <= s; i++) {
            add(ans, dp[n][i][2][2]);
        }
        printf("%lld\n", 1LL * 4 * ans % MOD);
    }
    return 0;
}

J
題意:每次可以選擇加一、不動、減一。選擇減的話,如果上次減去x的話這次要減2*x,反之減1。問你最少需要多少次p可以到q。

思路:我們有兩種決策——p一直升到大於q的最小值或者降到小於q的最大值,其實二者代價是相同的。我們就考慮降的策略時,中間記錄一下需要停頓的次數rest,最後一次加的時候取差值和rest最大值即可。因爲我們可以把停頓一次用做一次加操作。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
#include <cstring>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <set>
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pii;
const int MAXN = 2e5 + 10;
const int MOD = 110119;
void add(LL &x, LL y) { x += y; if(x < 0) x += MOD; x %= MOD; }
LL fac[64];
int Work(LL n) {
    int k = 0;
    while(fac[k] - 1 <= n) k++;
    return k;
}
LL Solve(LL p, LL q, LL ans, LL rest) {
    if(p <= q) {
        return ans + max(rest, q - max(0LL, p));
    }
    LL l = p - q; int k = Work(l);
    if(fac[k - 1] - 1 == l) {
        return ans + k - 1 + rest;
    }
    return min(Solve(p - fac[k-1] + 1, q, ans + k - 1, rest + 1), Solve(p - fac[k] + 1, q, ans + k, rest));
}
int main()
{
    fac[0] = 1LL;
    for(int i = 1; i <= 62; i++) {
        fac[i] = fac[i - 1] * 2;
    }
    int t; scanf("%d", &t);
    while(t--) {
        LL q, p; scanf("%lld%lld", &p, &q);
        if(p < q) {
            printf("%lld\n", q - p);
        }
        else {
            printf("%lld\n", Solve(p, q, 0, 0));
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章