2017 Multi-University Training Contest 2

来源:互联网 发布:淘宝订单自动关闭 编辑:程序博客网 时间:2024/06/05 10:07

1001 - Is Derek lying?

Problem

N 个问题,每个问题只有 A, B, C 三种选项。已知 Derek 和 Alfia 对每题的回答以及对应的最终得分(每题答对 1 分,错误 0 分)。问最终两人的得分是否一定错误。

Idea

根据两人的回答统计回答相同的题数 same 和不同的题数 diff 。记两人的得分分别为 x 和 y 。

  • 得分和 - same > N ,一定说谎。
  • x > diff 且 x-diff > y ,一定说谎。
  • y > diff 且 y-diff > x ,一定说谎。
  • 否则可能是真的。

Code

#include<bits/stdc++.h>using namespace std;const int N = 80000 + 10;int T, n, x, y;char a[N], d[N];int main(){    scanf("%d", &T);    while(T-- && scanf("%d %d %d", &n, &x, &y)!=EOF)    {        scanf(" %s %s", a, d);        int same = 0, diff;        for(int i=0;i<n;i++)            if(a[i] == d[i])    same++;        diff = n - same;        if(x > diff && x-diff > y) {            printf("Lying\n");        } else if(y > diff && y-diff > x) {            printf("Lying\n");        } else if(x+y - same > n) {            printf("Lying\n");        } else {            printf("Not lying\n");        }       }}

1003 - Maximum Sequence

Problem

已知 a1,a2,,anb1,b2,,bn ,求构造 an+1,,a2n ,使得 2nn+1ai 最大。

构造限制为每次构造新的 ai 需选择一个未选择过的 bk ,使得满足 aimax(ajjbkj<i)

Idea

由于每个 bk 都能使得新的 ai 产生最大不超过 [bk, i) 区间最大 a 值。

故贪心优先使用剩余的最小的 bk ,使得构造的 ai 最大。需要注意的是在第一次操作后,an+1 也将产生,可以用于作为最大参照构造后续 ai

Code

#include<bits/stdc++.h>using namespace std;const int N = 250000 + 10;const int mod = 1e9 + 7;int n, a[N], b[N], mdfa[N];int main(){    while(scanf("%d", &n)!=EOF)    {        for(int i=1;i<=n;i++)            scanf("%d", &a[i]);        for(int i=1;i<=n;i++)            scanf("%d", &b[i]);        sort(b+1, b+n+1);        mdfa[n+1] = 0;        for(int i=n;i;--i)            mdfa[i] =  max(mdfa[i+1], a[i]-i);        int amore = mdfa[b[1]];        long long ans = amore;        amore -= n+1;        for(int i=2;i<=n;i++)        {            if(mdfa[ b[i] ] > amore) {                ans += mdfa[b[i]];            } else {                ans += amore;            }            if(ans >= mod)  ans %= mod;        }           printf("%lld\n", ans);    }}

1004 - Puzzle

Problem

A Jigsaw puzzle contains NM1 pieces of jigsaws in a N rows ×M columns rectangular board.Each jigsaw has a distinct number from 1 to NM1.Li is a naughty boy,he wants to arrange the board in his unique way.At the beginning,he picks all NM1 jigsaws out and put them on the table and then he will put them back to the board respecting the following steps:
1.Sorting all the remaining jigsaws on the table in ascending order.
2.Picking out the 1st ,the P+1 th ,the 2*P+1 th,……the n*P+1 th jigsaws and put them back to the blank area in the board one by one from the top row to the bottom row,from the left column to the right column.
3.if there are jigsaws remained on the table,back to step 1.
After he arranging the board,it’s obvious that there’s only one blank area located at the bottom-right corner.
Your task is to make the numbers on jigsaws sorted with every row and every column in ascending order(From left to right,top to bottom),and the blank area should be located at the bottom-right corner in the end.Each step you can move the blank area’s neighboring jigsaws(which share a common side with the blank area) towards the blank area.It’s really a difficult question,so you need to write a program to judge whether it is possible to complete the task.

Idea

具体思路请参照 官方题解

说实话本来想更暴力的,结果出题人不良心,INPUT 表明 T100 ,当实际上 T=1000

Code

#include<bits/stdc++.h>using namespace std;int T, n, m, p;int main(){    scanf("%d", &T);    while(T-- && scanf("%d %d %d", &n, &m, &p)!=EOF)     {        if(p%2) {   printf("YES\n");    continue;   }        int cntStep = 0,    lft = n*m-1,    item;        while(lft>0) {            item = (lft+p-1) / p;            lft -= item;            if(item%4==0 || item%4==1)  continue;            cntStep++;        }        printf("%s\n", cntStep%2?"NO":"YES");    }}

1006 - Funny Function

Problem

Fx,y 满足下列公式:

F1,1=F1,2=1

F1,i=F1,i1+2×F1,i2(i3)

Fi,j=j+N1k=jFj1,k(i2,j1)

给定 N 和 M,求 Fm,1109+7 取模。

Limit

1N,M<263

Idea

官方题解请见:LINK

个人偷懒直接找规律用特征数列 AC 。

对应每个 m ,

当 n = 2 时数列:1, 2, 6, 18, 54…

当 n = 3 时数列:1, 5, 33, 229, 1601…

当 n = 4 时数列:1, 10, 150, 2250…

当 n = 5 时数列:1, 21, 641, 19861…

存在一个特征数列 1, 5, 33, 229, 1601, 11205, 78433, 549029, 3843201, 26902405, 188316833, 1318217829, 9227524801, 64592673605,
452148715233, 3165041006629, 22155287046401, 155087009324805, 1085609065273633, 7599263456915429, 53194844198408001

的通项公式为 2×7n+13 ,而 n=3 时,231=7

故 n 为奇数时,2×(2n1)m113

n 为偶数时,2×(2n1)m13

HINT:对于特征数列的积累,可以经常逛逛 oeis 。

Code

#include<bits/stdc++.h>using namespace std;const int mod = 1e9 + 7;const int maxn = 5, maxm = 5;int T;long long n, m;long long pow_mod(long long a, long long i) {    long long ans = 1;    while(i >= 1) {        if(i&1) (ans *= a) %= mod;        i >>= 1;        (a *= a) %= mod;    }    return ans;}int main(){    scanf("%d", &T);    while(T--)    {        scanf("%lld %lld", &n, &m);        if(m == 1 || n == 1) {            printf("1\n");  continue;        }        long long mul = (pow_mod(2, n)-1+mod) % mod;        long long fin = 2 * pow_mod(mul, m-1) % mod;        if(n%2) fin++;        (fin *= 333333336) %= mod;        printf("%lld\n", fin);    }}

1008 - To my boyfriend

Problem

对于给定的 N×M 的矩阵,统计所有子矩阵含有的不同数字的个数,求和并对总子矩阵个数作商。

Idea

统计每种数字对多少子矩阵作贡献。每个子矩阵中多个相同数字贡献只记一次。

由于 1N,M100 。故考虑暴力枚举每个点 (x, y) 。统计以 (x, y) 为任意矩阵中所有该数字最左上点的矩阵个数。

具体统计方案为:

  • 已知任意矩阵下边界 >=x 均满足条件,故下边界的方案有 (n-x+1) 种。
  • 对于上边界,从 up=xup=1 逐一枚举,在过程中维护其左右边界的取值方案。
  • 每种上边界的枚举有 (x, y) 所代表的数字对 (n-x+1) * (y-Left+1) * (Right-y+1) 个矩阵都存在贡献。

Code

#include<bits/stdc++.h>using namespace std;const int N = 100+10, M = 100+10;int T, n, m, g[N][M];long long calc(int x, int y) {    int num = g[x][y], lft = 1, rgt = m;    long long ret = 0;    for(int up=x;up;up--)    {        if(up!=x && g[up][y] == num)    break;        for(int l=y-1;l>=lft;l--)            if(g[up][l] == num) {   lft = l+1;  break;  }        if(up!=x) {            for(int r=y+1;r<=rgt;r++)                if(g[up][r] == num) {   rgt = r-1;  break;  }        }        ret += (n-x+1ll) * (y-lft+1ll) * (rgt-y+1ll);    }    return ret;}int main(){    scanf("%d", &T);    while(T-- && scanf("%d %d", &n, &m)!=EOF)    {        for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)            scanf("%d", &g[i][j]);        long long ans = 0, tot = 0;        for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)            ans += calc(i, j),            tot += i*j;        printf("%.9lf\n", ans*1.0/tot);    }}

1009 - TrickGCD

Problem

对于给定的 A 数组,求有多少种不同的构造方案,使得 B 数组满足:

  • 1BiAi
  • 对于任意区间 [l, r]gcd(bl,bl+1,,br)2

Limit

1n,Ai105

Idea

对任意区间 gcd 结果 2 等价于区间 [1, n] 种所有 bi 的 gcd 结果为大于等于 2.

枚举每一个合法的 gcd 值 2~min(A[i]) 。利用容斥,其中含奇数个质因子的对结果的贡献为正,含偶数个质因子的对结果贡献为负,忽略形如 prime2×factor 的 gcd 枚举值。

对每个枚举的 gcd ,可以知道 Ai[gcd2,gcd31] 对该 gcd 结果的方案贡献为 2countAi[gcd3,gcd41] 对结果贡献为 3count 。如此枚举统计的总复杂度为 O(n+n2+n3++nn)O((n(lnn+C)) 。其中应优先统计每个 Ai 的个数,并作前缀维护,得到 O(1) 获取每个 count

Code

#include<bits/stdc++.h>using namespace std;const int N = 100000 + 10;const long long mod = 1e9 + 7;int T, n, a[N], ca[N], mn, mx, cnt[N];bool isSqr[N];void prime() {    memset(isSqr, false, sizeof(isSqr));    for(int i=2;i<=100000;i++) {        if(cnt[i])  continue;        for(int j=i;j<=100000;j+=i) {            cnt[j]++;            if(j % ((long long)i*i) == 0)   isSqr[j] = 1;        }    }}long long pow_mod(long long a,long long i) {    long long ans = 1;    while(i >= 1) {        if(i&1) (ans *= a) %= mod;        i >>= 1;        (a *= a) %= mod;    }    return ans;}int calFac(int gcd) {    long long ret = 1;    for(int i=2;i<=100000;++i) {        if(gcd * i > mx)    return ret;        int num = ca[min(100000, gcd*(i+1)-1)] - ca[gcd*i-1];        if(num==0)  continue;        ret *= pow_mod(i, num);        if(ret >= mod)  ret %= mod;    }}   int main(){    prime();    scanf("%d", &T);    for(int ica=1;ica<=T && scanf("%d", &n)!=EOF;ica++)    {        memset(ca, 0, sizeof(ca));        mx = 0, mn = 100000;        for(int i=1;i<=n;i++)        {            scanf("%d", &a[i]);            ca[ a[i] ]++;            mn = min(a[i], mn);            mx = max(a[i], mx);        }        if(mn == 1) {            printf("Case #%d: 0\n", ica);   continue;        }        for(int i=1;i<=100000;i++)            ca[i] += ca[i-1];        int ans = 0, fac;        for(int i=2;i<=mn;i++)        {            if(isSqr[i] == 1)   continue;            fac = calFac(i);            if(cnt[i]%2)    ans += fac;            else    ans -= fac;            ans %= mod;        }        (ans += mod) %= mod;        printf("Case #%d: %d\n", ica, ans);    }}

1011 - Regular polygon

Problem

二维坐标系上的 N (N500) 个整数点,求有多少正多边形。

Idea

由于每种正多边形的顶角度数已知,可以简单考虑得出在已知一边的情况下构造的正多边形(除正方形外)均有点将落在非整数点上。

故只需统计正方形个数。枚举任意两个点,构造剩余两个点并 O(logN) 判断是否存在。需注意去重。

Code

#include<bits/stdc++.h>using namespace std;int n, x[550], y[550];map<pair<int, int>, int> mp;pair<int, int> p, q;int main(){    while(scanf("%d", &n)!=EOF)    {        mp.clear();        for(int i=1;i<=n;i++)        {            scanf("%d %d", &x[i], &y[i]);            mp[make_pair(x[i], y[i])] = i;        }        long long ans = 0;        for(int i=1, dx, dy;i<=n;i++)        for(int j=i+1;j<=n;j++)        {            if(i == j)    continue;            p = make_pair(x[i]+(y[j]-y[i]), y[i]+(x[i]-x[j]));            q = make_pair(x[j]+(y[j]-y[i]), y[j]+(x[i]-x[j]));            if(mp.find(p) != mp.end() && mp.find(q) != mp.end())                ans++;            p = make_pair(x[i]-(y[j]-y[i]), y[i]-(x[i]-x[j]));            q = make_pair(x[j]-(y[j]-y[i]), y[j]-(x[i]-x[j]));            if(mp.find(p) != mp.end() && mp.find(q) != mp.end())                ans++;        }        printf("%lld\n", ans / 4);    }}