2017 Multi-University Training Contest

来源:互联网 发布:阿里云 微信小程序 编辑:程序博客网 时间:2024/06/04 00:57

  这场我实在是补不动…..

HDU - 6085 - Rikka with Candies(bitset优化)

题意:
  给若干个k的查询,输出ai%bj==k的种类数mod2的结果。ai,bj,查询次数均为五万。数字大小范围也是五万。
思路:
  我们枚举从大到小枚举k,也就是说从max{b}开始枚举到0。因为ai%bj==k等价于(aik)%bj==0,那么统计k的结果应该就是for(int i = 0; i < a.size(); i++) ans += cnt[a[i]-k];。所以对于每一个枚举到的k,都应该对他的倍数x进行cnt[x]++操作。
  以上应该是暴力的做法,但是显然是会T的,不过由于题目要求的是mod2意义下的答案,所以可以用bitset进行优化。怎么优化呢,首先用一个bitset,ai,表示a数组有哪些数字。然后用一个bitset,bj,起到cnt数组的效果,只需要翻转对应位即可,因为mod2意义下的加法和它是等价的。统计答案的时候只需要(ai >> k) & bj).count() & 1。ai右移k,即-k,&上bj并count,即看有哪些数字满足(aik)%bj==0
  

#include <bits/stdc++.h>using namespace std;const int maxn = 50000 + 5;int b[maxn], ans[maxn];bitset<maxn>ai, bj;int main(){    int T;    scanf("%d", &T);    while(T--)    {        ai.reset(), bj.reset();        int n, m, k;        scanf("%d%d%d", &n, &m, &k);        for(int i = 0; i < n; i++)        {            int x;            scanf("%d", &x);            ai.set(x);        }        for(int i = 0; i < m; i++)  scanf("%d", &b[i]);        sort(b, b + m);        int bMax = b[m - 1];        int cnt = m - 1;        for(int i = bMax; i >= 0; i--)        {            ans[i] = ((ai >> i) & bj).count() & 1;            if(cnt >= 0 && b[cnt] >= i)            {                for(int j = 0; j <= bMax; j += b[cnt])  bj.flip(j);                cnt--;            }        }        for(int i = 0; i < k; i++)        {            int x;            scanf("%d", &x);            printf("%d\n", ans[x]);        }    }    return 0;}

F - Rikka with Graph HDU - 6090(找规律计数)

题意:
给你n个孤点,让你添加m条边(n1e6,m1e12)。定义dist(i,j)为两点间最短距离,如果不可达,则为n。求解minni=1nj=1dist(i,j)
思路:
  显然可以发现,n>m的时候,定义一个特殊点,由它不停的连向其他点,是最优的策略。直到形成一个n-1叉树。这个过程的ni=1nj=1dist(i,j)很容易求得,就是一个初始值,和一个等差数列的加减法。
  当nm的时候,每多一条边,其实就是减少1。over。

#include <bits/stdc++.h>using namespace std;int main(){    int T;    scanf("%d", &T);    while(T--)    {        long long n, m;        scanf("%lld%lld", &n, &m);        m = min(m, n * (n - 1) / 2);        if(m < n)        {            printf("%lld\n", n * n * (n - 1) - (2 * n - 2 + (m - 1) * (n - 2)) * m);        }        else        {            long long d = m - n + 1;            m = n - 1;            long long ans = (n * n * (n - 1) - (2 * n - 2 + (m - 1) * (n - 2)) * m) / 2;            printf("%lld\n", (ans - d) * 2);        }    }    return 0;}

H - Rikka with Subset HDU - 6092 (01背包逆过程)

题意:
  有一个a数组 (n50),一个b数组,长度(m10000)。b[i] 表示元素和为 i 的集合个数,让你求 a[],并按照其字典序最小输出。
思路:
  其实如果,倒过来就是一个01背包的问题。已知a数组,让你求解b数组。所以这题就是一个倒过来的01背包。复杂度别估计错了,进入while循环的次数只有n次。O(nm)

#include <bits/stdc++.h>using namespace std;long long dp[10000 + 5];int main(){    int T;    scanf("%d", &T);    while(T--)    {        memset(dp, 0, sizeof(dp));        int n, m;        scanf("%d%d", &n, &m);        vector<long long>b;        b.resize(m + 1);        for(int i = 0; i <= m; i++)  scanf("%lld", &b[i]);        vector<int>ans;        for(int i = 1; i <= m; i++)        {            int x = b[i] - dp[i];            while(x > 0)            {                ans.push_back(i);                x--;                for(int j = m - i; j >= 0; j--)                {                    dp[j + i] += dp[j];                }                dp[i]++;            }        }        for(int i = 0; i < ans.size(); i++)        {            printf("%d%c", ans[i], i == ans.size() - 1 ? '\n' : ' ');        }    }    return 0;}

K - Rikka with Competition HDU - 6095(water)

题意:
  出n个数,进行n-1场比赛,每场比赛如果双方值差的绝对值大于k就大的数赢,否则都有可能赢,问最后最多多少人可能赢。
思路:
  简单题,排个序,从大到小,找一条最长链:满足俩俩之差不超过k。就能通过第二个胜第一个,第三个胜第二个,…,这种策略来达到使得这条链上任意一个人获胜。输出长度即可。

#include <bits/stdc++.h>using namespace std;int a[100000 + 5];int main(){    int T;    scanf("%d", &T);    while(T--)    {        int n, k;        scanf("%d%d", &n,&k);        for(int i = 0; i < n; i++)  scanf("%d", &a[i]);        sort(a, a + n);        int ans = 1;        for(int i= n - 1; i >= 1; i--)        {            if(a[i] - k > a[ i -  1])  break;            ans++;        }        printf("%d\n", ans);    }    return 0;}