2017 Multi-University Training Contest

来源:互联网 发布:2016乒超联赛网络 编辑:程序博客网 时间:2024/06/13 23:45

HDU - 6045- Is Derek lying?(思维题)

题意:

  n个数,给出A的分数,B的分数,对一题得一分,然后给出每个人每道题的选择,每个题目有ABC三个选项,问是否存在这种可能

思路:

  找出两个人的选择不同的数量diff。然后计算两个人的得分之差。如果能用diff去弥补,那么就存在这种可能性。反之则不行。

  然后还要判断一下,两者分数是否过高。画个图,“尽量错”使得分数总和有可能达到,也是通过diff去考虑。

 

#include <bits/stdc++.h>using namespace std;const int maxn = 80000 + 5;char a[maxn];char b[maxn];int main(){    int T, n, x, y;    scanf("%d", &T);    while(~scanf("%d%d%d", &n, &x, &y))    {        scanf("%s", a);        scanf("%s", b);        int diff = 0;        for(int i = 0; i < n; i++)        {            if(a[i] != b[i])    diff++;        }        int delta = abs(x - y);        if(delta <= diff && 2 * n - diff >= x + y)   puts("Not lying");        else puts("Lying");    }    return 0;}

HDU - 6047 - Maximum Sequence(贪心)

题意:    

        给定数组a和数组b的前n项,让你根据以下公式得到补全数组a[n + 1] - a[2n]。

        从b数组中取出一个元素bk使得的值最大。

思路:

  实际上每次就是查询a[bk] - a[i - 1]之间{a[x] - x}的最大值,然后填到a[i]里。然后显然我们需要把b数组从小到大排序,来赋值比较好。这样贪心很显然。

  然后我们发现,a[n + 1] - a[i - 1]这有一右半段区间的数每一次都是要查询的,而且他是单调非递增的。思考一下为什么。因为你取最大值的区间变小了,那么最大值一定是不会超过原来的最大值的,而且你减去的东西也变大了。甚至是一个单调递减的吧。所以这一块的最大值应该就是a[n + 1]。

  那左半区间a[bk] - a[n]的最大值怎么查呢,我们可是要查250000次的呢。这里我们可以做到O(1)的查询。用什么呢?

  你想啊这左半区间的查询最大值,是不是一个右端点固定左端点不断增大的过程。那么从右边a[n]开始维护一个最大值的数组left,是不是可以做到left[i] : 是从a[i] - a[n]的最大值。left[i] = max(a[i], left[i + 1])。是不是还算巧妙的方法。

 

#include <bits/stdc++.h>using namespace std;const int maxn = 500000 +5;const int mod = 1e9 + 7;int a[maxn];int b[maxn];int l[maxn];int r[maxn];int main(){    int n;    while(~scanf("%d", &n))    {        for(int i = 1; i <= n; i++)  scanf("%d", &a[i]);        for(int j = 1; j <=n; j++)   scanf("%d", &b[j]);        l[n] = a[n] - n;        for(int i = n - 1; i >= 1; i --)        {            l[i] = max(l[i + 1], a[i] - i);        }        sort(b + 1, b + n);        long long ans = 0;        for(int i = 1; i <= n; i++)        {            int idx = b[i], val;            if(i == 1)            {                val = l[idx];                a[n + i] = val;                r[i + n] = val - n- i;            }            else            {                val = max(l[idx], a[n + 1] - n - 1);                a[n + i] = val;            }            ans = ans +val;            ans %= mod;        }        printf("%lld\n", ans);    }    return 0;}

HDU - 6050 - Funny Function(数学公式推导)

题意:

  给定N,M。(1e18)。求F(M,1)。

思路:

  公式推导,利用数列知识推得F(1, i)的通项公式,然后可以尝试的写一下F(2,j),会发现可以利用等比数列求和公式算出答案,同理F(3,j)。就会发现规律了。这题说的很简略,有困惑的可以留言交流。

 

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int mod = 1e9 + 7;//求余版本LL quickPow(LL a, LL b, LL Mod){    LL ret = 1;    while(b)    {        if(b & 1)   ret = ret * a % Mod;        b >>= 1;        a = (a * a) % Mod;    }    return ret;}int main(){    int T;    scanf("%d", &T);    while(T--)    {        LL n, m;        scanf("%lld%lld", &n, &m);        LL ans;        LL inv3 = quickPow(3, mod - 2, mod);        LL temp = quickPow(2, n, mod);        temp --;        if(n & 1)        {            ans = (2LL * quickPow(temp, m - 1, mod) + 1) % mod * inv3 % mod;        }        else        {            ans = 2LL * quickPow(temp, m - 1, mod) % mod * inv3 % mod;        }        printf("%lld\n", ans);    }    return 0;}

 

HDU - 6052 - To my boyfriend(计数问题)

题意:

  给出一个N*M的数字矩阵。求子矩阵的不同数字的个数的期望。(限制 N,M <= 100)

思路:

  枚举每个格子对答案的贡献度。我们若想要不重复的计数,我们要怎么办呢。

  规定:每个矩形当中同一种颜色,只会对答案贡献1,那么规定矩形中同色的,只有自左向右,自上向下的第一个对这个矩形有贡献。

  那么假设我们枚举到点(x,y),我们枚举高度h(x >= h >= 1),然后向左向右探测,找到左右边界lb,rb。此时。此时(x,y)对(y - lb + 1) * (rb - y + 1)*(n - x + 1)个矩形有贡献。怎么理解呢。枚举了一个高度h的时候在第h行上,位于点(x,y)左边只有一排(y - lb + 1)个点可以作为矩形的左上顶点,(rb - y + 1)*(n - x + 1)个点可以作为右下顶点。这样就能算出来包含(x,y)的矩形有几个,即这个点对答案的贡献是多少。那么我们每次calc一个点(x,y)的复杂度是枚举高度*枚举左右边界,O(n^2)。又要进行O(n^2)次calc。那么总的复杂度是O(n^4)。

  但是呢我们可以继续加速这个过程。做到O(n^3),有兴趣的同学可以搜一下别的题解。

 

#include <bits/stdc++.h>using namespace std;typedef long long LL;int ma[105][105];int n, m;LL solve(int x, int y){    LL ret = 0;    int color = ma[x][y];    int lb = 1, rb = m;    for(int i = x; i >= 1; i--)    {        if(i != x && ma[i][y] == color) break;        int l = y, r = y;        for(int j = y - 1; j >= max(1, lb); j--)        {            if(ma[i][j] == color)   break;            l = j;        }        lb = max(l, lb);        if(i == x)        {            ret += 1LL * (y - lb + 1) * (n - x + 1) * (rb - y + 1);            continue;        }        for(int j = y + 1; j <= min(m, rb); j++)        {            if(ma[i][j] == color)   break;            r = j;        }        rb = min(r, rb);        ret += 1LL * (y - lb + 1) * (n - x + 1) * (rb - y + 1);    }    return ret;}int main(){    int T;    scanf("%d", &T);    while(T--)    {        scanf("%d%d", &n, &m);        for(int i = 1; i <= n; i++)        {            for(int j = 1; j <= m; j++)            {                scanf("%d", &ma[i][j]);            }        }        LL ans = 0;        for(int i = 1; i <= n; i++)        {            for(int j = 1; j <= m; j++)            {                ans += solve(i, j);            }        }        LL tot = 0;        for(int i = 1; i <= n; i++)        {            for(int j = 1; j <= m; j++)            {                tot += 1LL * i * j;            }        }        printf("%.9f\n", 1.0 * ans / tot);    }    return 0;}

HDU - 6053- TrickGCD(容斥 莫比乌斯反演)

题意:

  给你n个数字,b数组每个位置i的数字b[i]可以小于等于a[i],求所有区间(l,r)都满足gcd(l,r)大于等于2的情况数的b数组的方案数。

思路:

  这题有两种做法,莫比乌斯反演的做法欢迎留言交流了,因为都是数学公式,写出来比较麻烦。如果枚举b数组整体gcd的结果为x,那么显然对于x有种可能。但是这个时候会有重复计数的问题。我们可以发现这个时候,恰好容斥所需要的系数是莫比乌斯函数的相反数,所以乘上这个系数就能做到容斥了。可是复杂度还是一个O(n^2)的级别呢。我们采取分块+快速幂的策略。利用的是怎么个思想呢,对于[kg,(k+1)g)区间的数字而言,/g的结果均为k,所以我们可以变成枚举b数组整体gcd的结果i,for(int i = 2; i <= minn; i++)。

然后查询他的每个倍数j,for(int j = i; j <= maxx; j += i)。利用前缀和预处理以后可以在O(1)的时间内得到整个区间/g为1的数字的数量,为2的数字的数量。然后通过快速幂计算出来。这个地方把n^2的复杂度优化到了nlognlogn的级别。

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int mod = 1e9 + 7;const int maxn = 1e5 + 5;int a[maxn];int pre[maxn];//线性筛法求莫比乌斯函数bool check[maxn];int prime[maxn];int mu[maxn];void Moblus(){    memset(check,false,sizeof(check));    mu[1] = 1;    int tot = 0;    for(int i = 2; i < maxn; i++)    {        if( !check[i] ){            prime[tot++] = i;            mu[i] = -1;        }        for(int j = 0; j < tot; j++)        {            if(i * prime[j] > maxn) break;            check[i * prime[j]] = true;            if( i % prime[j] == 0){                mu[i * prime[j]] = 0;                break;            }else{                mu[i * prime[j]] = -mu[i];            }        }    }}LL quickPow(LL a, LL b, LL Mod){    LL ret = 1;    while(b)    {        if(b & 1)   ret = ret * a % Mod;        b >>= 1;        a = (a * a) % Mod;    }    return ret;}int main(){    Moblus();    int T;    scanf("%d", &T);    int cas = 1;    while(T--)    {        memset(pre, 0, sizeof(pre));        int n;        scanf("%d", &n);        int minn = 0x3f3f3f3f;        int maxx = -1;        for(int i = 0; i <n; i++)        {            scanf("%d", &a[i]);            minn = min(minn, a[i]);            maxx = max(maxx, a[i]);            pre[a[i]]++;        }        long long ans = 0;        for(int i = 1; i < maxn; i++)   pre[i] += pre[i - 1];        for(int i = 2; i <= minn; i++)        {            int cnt = 1;            LL temp = 1;            if(mu[i] == 0)  continue;            for(int j = i; j <= maxx; j += i)            {                int r = min(maxx, j + i - 1);                int l = j;                temp = temp * quickPow(cnt, pre[r] - pre[l - 1], mod) % mod;                cnt ++;            }            temp = (temp * -mu[i] + mod) % mod;            ans = (ans + temp) % mod;        }        printf("Case #%d: %lld\n", cas++, ans);    }    return 0;}

HDU - 6055 - Regular polygon(结论题)

题意:

  给你n个点的坐标,坐标以整型输入,问所有的点能组成多少个正多边形。(n<=500)

思路:

  有一个结论:整点组成的正多边形只能是正四边形。

  那么我们需要知道n个点能组成多少个正四边形。我们直接枚举两个点作为正方形的邻边,check就行了。这题是暴力枚举。至于,由两个点枚举的邻边推出剩下两个点的坐标呢,我们知道一个正方形可以补四个全等三角形组成一个大正方形吧。当前正方形外面再套一个大的正方形,大正方形的边与坐标轴平行,然后就在x,y方向上挪动一定量就能得到另外两个点了,画一下图就懂了,初中几何问题。

  注意!!!check的时候,用的是vis[x][y]的check这个(x,y)这个整点是否输入了的时候,一定要注意,检查x,y是否越界了!!!。

#include <bits/stdc++.h>using namespace std;#define x first#define y secondconst int maxn = 500 + 5;pair<int, int>nodes[maxn];int vis[maxn][maxn];int main(){    int n;    while(~scanf("%d", &n))    {        memset(vis, 0, sizeof(vis));        for(int i = 0; i < n; i++)        {            int x, y;            scanf("%d%d", &x, &y);            x += 100, y += 100;            nodes[i] = {x, y};            vis[x][y] = 1;        }        int ans = 0;        for(int i = 0; i < n; i++)        {            for(int j = i + 1; j < n; j++)            {                pair<int, int>a = nodes[i], b = nodes[j];                int n = a.x - b.x;                int m = a.y - b.y;                if(0 <= a.x - m && 0 <= a.y + n && 0 <= b.x - m && 0 <= b.y + n && vis[a.x - m][a.y + n] && vis[b.x - m][b.y + n])  ans++;                if(0 <= a.x + m && 0 <= a.y - n && 0 <= b.x + m && 0 <= b.y - n && vis[a.x + m][a.y - n] && vis[b.x + m][b.y - n])  ans++;            }        }        printf("%d\n", ans / 4);    }    return 0;}


原创粉丝点击