第四届“图灵杯”NEUQ-ACM程序设计竞赛(团队赛)【solved 7 / 10】

来源:互联网 发布:异形1知乎 编辑:程序博客网 时间:2024/05/22 22:27

http://oj.acmclub.cn/contest.php

问题 A: 谷神的赌博游戏(计数问题)

思路:

  都看成同余3的情况,那么有n+1个1,n个0 ,n个2。那么计算前缀和sum的时候,我们会发现只有三种情况:0,1,2.当前缀和为0的时候,我们可以添加1或者2,当前缀和为1的时候,我们可以添加1或者0,当前缀和为2的时候我们可以添加2或者0。

  又因为我们发现0插在哪里,都不会影响串的合法性,那么我们可以先不考虑0。先只考虑1和2,我们会发现,如果让1打头以后,一定是112 然后12交替,类似112121212的串。只有一种情况。如果让2打头一定是221212121,但是考虑到1比2多一个,所以最后一定是会不合法的,自己画一下就知道了。那么我们得到不包含0的时候我们只有一个合法串,那就是112121212,这种串吧 串长为n+1+n n个2排列是 n!,n+1个1的排列是(n+1)!,那么 1121212121 这个串在变成123456789这种数字的时候 应该n!*(n+1)!种吧。

  然后0不能插在第一个1的右边,其他都能插,就变成 n个0插到2n+1个位置,每个位置0的个数大于等于0。这是一个计数基础问题,看过大白书第二章的应该都会,答案就是x1+x2+....+x(2n+1)=n.    xi >= 0,那就是组合数C(3n, n)。再考虑到0也有n个,有n!种排列。(3n+1)!/ ( n! * (n + 1)! * C(3n, n) *n! )

#include<bits/stdc++.h>using namespace std;typedef long long LL;int main(){    int T;    scanf("%d", &T);    while(T--)    {        LL a[20];        int cas;        scanf("%d", &cas);        for(int i = 0; i < 10; i++) scanf("%lld", &a[i]);        sort(a, a + 10);        set<LL>s;        LL ans = a[9];        for(int i = 9; i >= 0; i--)        {            s.insert(a[i]);            if(s.size() == 3)   ans = a[i];        }        printf("%d %lld\n", cas, ans);    }    return 0;}

问题 B: 一个简单的问题(water)

#include<bits/stdc++.h>using namespace std;typedef long long LL;int main(){    int T;    scanf("%d", &T);    while(T--)    {        LL a[20];        int cas;        scanf("%d", &cas);        for(int i = 0; i < 10; i++) scanf("%lld", &a[i]);        sort(a, a + 10);        set<LL>s;        LL ans = a[9];        for(int i = 9; i >= 0; i--)        {            s.insert(a[i]);            if(s.size() == 3)   ans = a[i];        }        printf("%d %lld\n", cas, ans);    }    return 0;}


 


问题 C: 来简单地数个数(大数)

 

思路:

  懒得写大数....反正平时怎么做的,这题就怎么做,打个表,二分一下。只不过需要在大数下做。

 


问题 D: 简单的图形输出(递归?分形。)

 

 

#include<bits/stdc++.h>using namespace std;char s[1030][2050]; void show(int x, int y, int n){    if(n == 1)    {        s[x][y] = '/', s[x][y + 1] = '\\';//, s[x][y +2] = '\n';        s[x + 1][y - 1] = '/', s[x + 1][y] = s[x + 1][y + 1] = '_', s[x + 1][y + 2] = '\\';//, s[x + 1][y + 3] = '\n';        return ;    }    int blank = (1 << (n - 1));    show(x, y, n - 1);    show(x + blank, y - blank, n - 1);    show(x + blank, y + blank, n - 1);} int main(){    int n;    while(~scanf("%d", &n))    {        if(n == 0)  break;        for(int i = 1; i <= (1 << n); i++)        {            for(int j = 1; j <= (1 << (n + 1)); j++)            {                s[i][j] = ' ';            }        }        show(1, (1 << n), n);         int k =(1 << n) + 1;        for(int i = 1; i <= (1 << n); i++, k++)        {            for(int j = 1; j <= k; j++)            {                putchar(s[i][j]);            }puts("");        }        puts("");    }    return 0;}


问题 E: 简单的 RMQ(ST表)

题意:

  大白书原题。要求的是区间最大的查询,不涉及修改,那么可以联想到st表,因为是一个单调序列,所以相同数字一定是放在一起的。那么是不是显然,求出每段不同数字的长度,然后用st表来搞一下。再记录一下每段的左右边界,把输入的left和right,转换成对应的数字段号,就能查询了。

 

#include<bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1e5 + 5;int d[maxn][18];int a[maxn], l[maxn], r[maxn], num[maxn], belong[maxn];void initRMQ(int n, int a[]){    for(int i = 0; i < n; i++)  d[i][0] = a[i];    for(int j = 1; (1 << j) <= n; j++)    {        for(int i = 0; i + (1 << j) - 1 < n; i++)        {            d[i][j] = max(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);        }    }} int query(int l, int r){    int k = 0;    while((1 << (k + 1)) <= r - l + 1)    k++;    return max(d[l][k], d[r - (1 << k) + 1][k]);} int main(){    int n, q;    while(~scanf("%d", &n))    {        if(n == 0)  break;        memset(l, -1, sizeof(l));        memset(r, -1, sizeof(r));        memset(num, 0, sizeof(num));        memset(belong, -1, sizeof(belong));        scanf("%d", &q);        int cnt = -1;        for(int i = 0; i < n; i++)        {            int x;            scanf("%d", &x);            a[i] = x;            if(i == 0 || a[i] != a[i - 1])            {                cnt++;                l[cnt] = r[cnt] = i;            }            else            {                r[cnt] = i;            }            belong[i] = cnt;        }        cnt++;        for(int i = 0; i < cnt; i++)        {            num[i] = r[i] - l[i] + 1;        }        initRMQ(cnt, num);        while(q--)        {            int ql, qr;            scanf("%d%d", &ql, &qr);            ql--, qr--;            int a = belong[ql], b = belong[qr];             if(a == b)            {                printf("%d\n", qr - ql + 1);            }            else if(b - a == 1)            {                printf("%d\n", max(r[a] - ql + 1, qr - l[b] + 1));            }            else            {                int ans = query(a+1, b-1);                ans = max(ans, max(r[a] - ql + 1, qr - l[b] + 1));                printf("%d\n", ans);            }        }    }    return 0;}


问题 F: 一道简单的递推题(矩阵快速幂)

题意:

  勘误!题目的递推式应该是F(m + 1) = A1 * F(m) + ... + An * F(m-n+1)。

  那么就是一个矩阵快速幂的裸题了。注意一个幂是LL,憋爆int了。还有一个矩阵快速幂的时候,0优化,for i,j,k,这种会T。for k,i,j的0优化能快很多。

 

#include <bits/stdc++.h>using namespace std;typedef long long LL; const LL mod = 1000000007;const int mSize = 100; struct Matrix{    long long v[mSize][mSize];    friend Matrix operator* (const Matrix& a, const Matrix& b)    {        Matrix c;        memset(c.v, 0, sizeof(c.v));        for (int k = 0; k < mSize; k++)        {            for (int i = 0; i < mSize; i++)            {                if(a.v[i][k] == 0)  continue;                for (int j = 0; j < mSize; j++)                {                    if(b.v[k][j] == 0)  continue;                    c.v[i][j] += a.v[i][k] * b.v[k][j] % (mod);                    c.v[i][j]  %= (mod);                }            }        }        return c;    }    friend Matrix operator^ (Matrix x, LL n)    {        Matrix ans;        for (int i = 0; i < mSize; i++)            for (int j = 0; j < mSize; j++)                ans.v[i][j] = (i == j);        while (n)        {            if (n & 1)                ans = ans * x;            x = x * x;            n >>= 1;        }        return ans;    }};  long long f[105], a[105];int main(){    LL n, k;    while(~scanf("%lld%lld", &n, &k))    {        for(int i = 0; i < n; i++)  scanf("%lld", &f[i]);        for(int i = 0; i < n; i++)  scanf("%lld", &a[i]);        if(k == 1)        {            printf("%lld\n", f[0]);            return 0;        }        else if(k == 2)        {            printf("%lld\n", f[1]);            return 0;        }        else        {            Matrix mat;            memset(mat.v, 0, sizeof(mat.v));            for(int i = 0; i < n; i++)  mat.v[0][i] = a[i];            for(int i = 1; i < n; i++)  mat.v[i][i - 1] = 1;             LL mi = k - n;            mat = mat ^ mi;            LL ans = 0;             for(int i = 0; i < n; i++)  ans += mat.v[0][i] * f[n - 1 - i] % mod, ans %= mod;            printf("%lld\n", ans);        }     }     return 0;}


问题 G: 那么大奶牛之神(据说是个= = 试错题)

 

 


问题 H: 简单的机械臂设计(splay伸展树。)

 

 


问题 I: 一道不简单的题目 (water)

 


问题 J: 简单的变位词(water)

题意: 

  如果组数相同,则比较两个组中,各自字典序最小的单词,按字典序。

 

#include<bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1e5 + 5; vector<string>vec[30000 + 4];map<string, int>ma; bool cmp(const vector<string>&a, const vector<string>&b){    if(a.size() != b.size())    return a.size() > b.size();    return a[0] < b[0];}int main(){    string s;    int cnt = 1;    int k = 0;    while(cin >> s)    {        int len = s.size();        string str = "";        string store = s;        sort(s.begin(), s.end());        for(int i =0; i < len; i++)            str += s[i];        if(ma[str] == 0)    ma[str] = cnt++;         int id = ma[str];        vec[id].push_back(store);    }    for(int i = 1; i <= cnt; i++)        sort(vec[i].begin(), vec[i].end());    sort(vec + 1, vec + 1 + cnt, cmp);     for(int i = 1; i <= cnt && i <= 5; i++)    {        printf("Group of size %d:", vec[i].size());        set<string>temp;        for(auto o : vec[i])    temp.insert(o);        for(auto o : temp)  cout << " " << o;        printf(" .\n");    }    return 0;}

 

阅读全文
0 0