2017 Multi-University Training Contest

来源:互联网 发布:国产电视机 知乎 编辑:程序博客网 时间:2024/06/10 14:02

补题补题

Is Derek lying?

题意:
Derek和Alfia分别做题,题目选项有A、B、C,现在他们所选的选项和所得的分告诉你,问他们有没有撒谎。

思路:判定他们的上下限即可,当D和A有一个人全对时,他们一样的肯定得分,不一样的肯定不得分,假设一样的选项为k个,不一样的为p个,那么他们总分之和一定小于k * 2 + p,两分之差肯定小于p。

#include <bits/stdc++.h>#define MAXN 800050using namespace std;int main() {    int T, n, x, y;    char ch1[MAXN], ch2[MAXN];    scanf("%d", &T);    while (T--) {        scanf("%d %d %d", &n, &x, &y);        getchar();        gets(ch1);        gets(ch2);        if (x > n || y > n || x < 0 || y < 0) {            puts("Lying");            continue;        }        int k = 0;        for (int i = 0; i < n; i++) {            if (ch1[i] == ch2[i]) {                k++;            }        }        int t1 = min(x, y);        int t2 = max(x, y);        int p = t2 - t1, m = n - k;        if (p <= m && t1 + t2 <= 2 * k + m) {            puts("Not lying");        } else {            puts("Lying");        }    }} 

Maximum Sequence

题意:
给一个序列ai,每次可以从b数组里任意选数表示可以从下标为1到n(这里的n,每次制造出来一个新的数n会增长)的数中任意选取一个数下标为j制造出一个数放置在a序列最后面,这个数的值为aj - j,现在问新制造出来的数的和最大是多少。
思路:
拿个优先队列维护一下可用值。
所给的b序列贪心每次取最小的即可。

#include <bits/stdc++.h>#define MAXN 500050#define ll long long#define MOD 1000000007using namespace std;struct node {    ll idx, val;    bool operator < (const node &a) const {        return val < a.val;    }};priority_queue<node> q;ll num[MAXN];void init() {    while (!q.empty()) {        q.pop();    }}int main() {    ll n, tmp;    while (~scanf("%lld", &n)) {        init();        for (ll i = 0; i < n; i++) {            scanf("%lld", &tmp);            q.push((node){i + 1, tmp - (i + 1)});        }        for (ll i = 0; i < n; i++) {            scanf("%lld", &num[i]);        }        sort(num, num + n);        ll ans = 0;        for (ll i = 0; i < n; i++) {            while (!q.empty() && q.top().idx < num[i]) {                q.pop();            }            ans += q.top().val;            ans %= MOD;            ll tt = q.top().val;            q.push((node){n + i + 1, tt - (n + i + 1)});        }        printf("%lld\n", ans);    }   }

Sdjpx Is Happy

题意:
给一个序列,你可以将这个序列分成任意块,每一个块可以随意排序,你可以将其中两个块换一次位置。
现在问你最多可以分成多少块

思路:
先暴力预处理将每个区间内,可分成块的个数处理出来,若区间最大值和最小值不等于区间长度,则为0,若相对于上一块区间出现了新的最小值,则只为1,因为只能排序不能在这个区间内再分块。
若没制造出新的,则可以等于上一个区间与该区间的块状和。
然后再枚举每一块区间,若满足区间内可拆,则枚举拆点将其拆开(因为有一次交换机会)。

有点像dp。。但是就是区间暴力枚举

#include <bits/stdc++.h>#define MAXN 3010#define ll long longusing namespace std;int mn[MAXN][MAXN], mx[MAXN][MAXN];int sum[MAXN][MAXN];int a[MAXN], la[MAXN];int main() {    int T, n;    scanf("%d", &T);    while (T--) {        scanf("%d", &n);        memset(sum, 0, sizeof(sum));        for (int i = 1; i <= n; i++) {            scanf("%d", &a[i]);            mn[i][i] = mx[i][i] = a[i];            sum[i][i] = 1;            la[i] = i;        }        for (int i = 1; i <= n; i++) {            for (int j = i + 1; j <= n; j++) {                 mn[i][j] = min(mn[i][j - 1], a[j]);                mx[i][j] = max(mx[i][j - 1], a[j]);            }        }        for (int i = 2; i <= n; i++) {            for (int j = 1; j + i - 1 <= n; j++) {                int e = i + j - 1;                if (mx[j][e] - mn[j][e] != e - j) {                    sum[j][e] = 0;                } else {                    if (mn[j][e] < mn[j][la[j]]) {                        sum[j][e] = 1;                    } else {                        sum[j][e] = sum[j][la[j]] + sum[la[j] + 1][e];                    }                    la[j] = e;                }            }        }        int ans = sum[1][n];        for (int i = 1; i <= n; i++) {            for (int j = i; j <= n; j++) {                if (sum[i][j] && (i == 1 || (sum[1][i - 1] && mn[1][i - 1] == 1))) {                    int p = mx[i][j];                    if (p == n || (sum[p + 1][n] && mx[p + 1][n] == n)) {                        for (int k = p; k > j; k--) {                            if (sum[k][p] && mn[k][p] == i) {                                ans = max(ans, sum[1][i - 1] + sum[j + 1][k - 1] + sum[p + 1][n] + 2);                            }                        }                    }                }            }        }        printf("%d\n", ans);    }    return 0;} 

Funny Function

题意:有三个算式现在让你求Fm,1

思路:
赛后跟机智的队友补题,暴力打表把10x10的数据打了出来。。竟然发现了规律

对于偶数
每行有f(n, m) = f(n - 2, 2) * (2n - 1)m1
每列有f(n, 2) = f(n - 2,2) * 4 + 2

对于奇数
每行有f(n, m) = f(n - 2, 2) * (2n - 1)m1
每列有f(n, 2) = f(n - 2,2) * 4 + 1

当n或者m为1时,都为1。
然后矩阵快速幂和快速幂求解即可。。

#include <bits/stdc++.h>#define N 2#define ll long long#define MOD 1000000007using namespace std;struct mat{    ll mapp[N][N];};mat res;ll rec[] = {2, 5};void output(mat a) {    printf("=======\n");    for (int i = 0; i < N; i++) {        for (int j = 0; j < N; j++) {            printf("%lld ", a.mapp[i][j]);        }        puts("");    }}ll update(ll a) {    while (a < 0) {        a += MOD;    }    return a;}mat mul_mat(mat a, mat b, ll mod, int p) {    mat c;//  output(a);//  puts("-----");//  output(b);    for (int i = 0; i < N; i++) {        for (int j = 0; j < p; j++) {            c.mapp[i][j] = 0;            for (int k = 0; k < N; k++) {                c.mapp[i][j] = update(c.mapp[i][j]);                c.mapp[i][j] += (a.mapp[i][k] * b.mapp[k][j]) % mod;                c.mapp[i][j] %= mod;            }        }    }    return c;}mat ksm_mat(mat n, ll k, ll mod, int p) {    mat t = n, ans = res;    while (k) {        if (k & 1) {            ans = mul_mat(t, ans, mod, p);        }        t = mul_mat(t, t, mod, 2);        k >>= 1;    }    return ans;}ll ksm(ll n, ll k, ll mod) {    ll temp = n, ans = 1;    while (k) {        if (k & 1) {            ans = update(ans);            ans = (ans * temp) % mod;        }        k >>= 1;        temp = (temp * temp) % mod;    }    return ans % mod;}ll get(ll n, ll opt) {    mat p = {4, 1, 0, 1};    if (opt) { //偶数         res = (mat){rec[0], 0, 2, 0};    } else {        res = (mat){rec[1], 0, 1, 0};    }    mat ans = ksm_mat(p, n, MOD, 1);    return ans.mapp[0][0];}ll getAns(ll n, ll m) {    if (n == 1 || m == 1) {        return 1;    }    ll ans;    if (n & 1) {        ll a = get(n / 2 - 1, 0);        ll b = get(n / 2 - 1, 1);        res = (mat){a, 0, -b, 0};        mat p = (mat){ksm(2, n, MOD) - 1, 1, 0, 1};//      output(p);        ans = ksm_mat(p, m - 2, MOD, 1).mapp[0][0];    } else {        ans = (get(n / 2 - 1, 1) * ksm(((ksm(2, n, MOD) - 1 + MOD) % MOD), m - 2, MOD)) % MOD;    }    ans = update(ans);    return ans;}int main() {    int T;    ll n, m;    scanf("%d", &T);    while (T--) {        scanf("%lld %lld", &n, &m);        printf("%lld\n", getAns(n, m));    }}

TrickGCD

题意:
给一个序列,对于序列的每个数,你可以取1-ai中的任意数,现在让你组成新的序列,问可以组成多少个满足每个区间都满足gcd >= 2。

思路:
用筛法每次枚举每个除数,算区间内的可以被除的数的情况,最后再容斥一下把多余的去掉即可。

#include <iostream>#include <vector>#include <map>#include <cstdlib>#include <stdio.h> #include <string.h>#define MAX_PRIME 200000#define ll long long#define MAXN 100005#define MOD 1000000007using namespace std;ll f[MAXN];ll sum[MAXN];void init() {    memset(sum, 0, sizeof(sum));    for (ll i = 0; i < MAXN; i++) {        f[i] = 1;    }}ll ksm(ll a, ll n) {    ll ans = 1, t = a;    while (n) {        if (n & 1) {            ans = (ans * t) % MOD;        }        t = (t * t) % MOD;        n >>= 1;    }    return ans;}int main(){    ll T, n, c, cas = 1;    while (~scanf("%lld", &T)) {        while (T--) {            init();            ll lastans = 0;            scanf("%lld", &n);            for (ll i = 0; i < n; i++) {                scanf("%lld", &c);                sum[c]++;            }            for (ll i = 1; i < MAXN; i++) {                sum[i] += sum[i - 1];            }            for (ll i = 2; i < MAXN; i++) {                if (sum[i - 1]) {                    f[i] = 0;                    continue;                }                for (ll j = i; j < MAXN; j += i) {                    ll k = min(i + j - 1, (ll)(MAXN) - 1);                    ll b = sum[k] - sum[j - 1];                    ll a = j / i;                    f[i] = (f[i] * ksm(a, b)) % MOD;                }            }            for (ll i = MAXN - 1; i >= 2; i--) {                for (ll j = i * 2; j <= MAXN; j += i) {                    f[i] = (f[i] - f[j] + MOD) % MOD;                }            }            for (ll i = 2; i < MAXN; i++) {                lastans = (lastans + f[i]) % MOD;            }            printf("Case #%lld: %lld\n", cas++, lastans);        }    }}

Regular polygon

题意:
一个200x200的坐标上有n个点整数点。问能组成多少个不同的正多边形。

题解:
其实题意都是废话。。求正多边形。其实就只有正方形的存在。。
直接判每四个点存不存在就好了。
(orz hdu卡精度搞到写个cos和sin都给卡了。。)

#include <stdio.h>#include <math.h>#include <string.h>#define MAXN 1005using namespace std;const double PI = acos(-1.0);bool mapp[MAXN][MAXN];struct point {    int x, y;   } p[MAXN];point xuanzhuan(point a, point b) {    point c;    c.x = -(a.y - b.y) + b.x;    c.y = a.x - b.x + b.y;    return c;}int solve(point a, point b) {    for (int i = 0; i < 4; i++) {        point c = xuanzhuan(a, b);        a = b;        b = c;        if (c.x < MAXN && c.x >= 0 && c.y < MAXN && c.y >= 0) {            if (!mapp[c.x][c.y]) {                return 0;            }        } else {            return 0;        }    }    return 1;}int main() {    int n, ans;    while (~scanf("%d", &n)) {        ans = 0;        memset(mapp, false, sizeof(mapp));        for (int i = 0; i < n; i++) {            scanf("%d %d", &p[i].x, &p[i].y);            p[i].x += 200;            p[i].y += 200;            mapp[p[i].x][p[i].y] = true;        }        for (int i = 0; i < n; i++) {            for (int j = 0; j < n; j++) {                if (i != j) {                    ans += solve(p[i], p[j]);                }            }        }        printf("%d\n", ans / 4);    }} /*90 00 11 01 12 00 22 22 44 2*/
原创粉丝点击