题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6655

题目


QQ和CC两个人玩牌,QQ先出牌,数字标记牌上的颜色,每个人不能出另一个人出过的牌,但是可以出自己出过的牌,若轮到某人出牌,但是某人手里没牌了或者不能出牌了,那么那个人就输了。问最后的赢家是谁。

样例t, QQ手里n≤1e5张牌,CC手里m≤1e5张牌,p=1是给出每个人手里的牌对应的数字<2^64(要用unsigned long long), p=2用程序生成每个人手里的牌对应的数字。n+m不超过1e7

 

解题思路:


suma:QQ能出的牌数,sumb:CC能出的牌数

对每个人统计每张牌的牌数,对于自己有但对方没有的牌,肯定是可以全部出掉的,加入对应的suma/sumb中。

找出双方都有的牌,若对于牌i,QQ有张,CC有张,组成pair, 对pair按照+从大到小排序,统计QQ和CC分别能出的牌数。

给出的标准题解如下:

我想是让+越大, 那么有都相对比较大,两者不仅大而且很均衡,因为对于i不知道是轮到A拿还是轮到B拿,要让无论谁拿拿到的收益都比较大才比较公平吧,这才是最优策略。当然不排除一些极端情况。

 

ac代码:


unordered_map也这么慢吗,还是我哪里写的很慢。。。1900ms左右,在超时的边缘徘徊?

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
typedef unsigned long long ll;
typedef unordered_map<ll, ll> MAP;
typedef pair<ll, ll> PAIR;
ll k1, k2, mod, x, suma, sumb, cnt1, cnt2, cnt3;
ll A[maxn], B[maxn];
PAIR same[maxn];
int t, n, m, p;
ll rng() {
    unsigned long long k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= k3 << 23;
    k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
    return k2 + k4;
}
void getArray(int p, MAP &a, MAP &b) // a,b 记录出现次数
{
    MAP vis1, vis2;
    if(p == 1)
    {
        for(int i = 0; i < n; i++)
        {
            scanf("%llu", &x);
            a[x]++;
            if(vis1[x] == 0) vis1[x] = 1, A[cnt1++] = x;
        }
        for(int i = 0; i < m; i++)
        {
            scanf("%llu", &x);
            b[x]++;
            if(vis2[x] == 0)vis2[x] = 1, B[cnt2++] = x;
        }
    }
    else
    {
        scanf("%llu %llu %llu", &k1, &k2, &mod);
        for(int i = 0; i < n; i++)
        {
            x = rng() % mod;
            a[x]++;
            if(vis1[x] == 0) vis1[x] = 1, A[cnt1++] = x;
        }
        scanf("%llu %llu %llu", &k1, &k2, &mod);
        for(int i = 0; i < m; i++)
        {
            x = rng() % mod;
            b[x]++;
            if(vis2[x] == 0) vis2[x] = 1, B[cnt2++] = x;
        }
    }
}
bool cmp(PAIR a, PAIR b)
{
    return a.first + a.second > b.first + b.second;
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    scanf("%d", &t);
    while(t--)
    {
        MAP a, b;
        cnt1 = cnt2 = cnt3 = 0;
        scanf("%d %d %d", &n, &m, &p);
        suma = n, sumb = m;
        getArray(p, a, b);
        for(int i = 0; i < cnt1; i++)
            if(b[A[i]])
            {
                same[cnt3++] = {a[A[i]], b[A[i]]};
                suma -= a[A[i]], sumb -= b[A[i]];
            }
        sort(same, same + cnt3, cmp);
        for(int i = 0; i < cnt3; i++)
        {
            if(i % 2 == 0) suma += same[i].first;
            else sumb += same[i].second;
        }
        if(suma > sumb) printf("Cuber QQ\n");
        else printf("Quber CC\n");
    }
    return 0;
}

附上一个800ms左右的代码,它没有直接记录每个数出现的次数,一个for循环专门统计了出现的次数,而且只用了pair。ORZ

博客地址https://blog.csdn.net/qq_41117236/article/details/99442068

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
typedef pair<int, int> P;
const int N=2e5+10;
P A[N],B[N],C[N];
ULL a[N];
ULL k1,k2;
ULL rng(){
    ULL k3=k1,k4=k2;k1=k4;k3^=k3<<23;k2=k3^k4^(k3>>17)^(k4>>26);
    return k2+k4;
}
void init(P T[],int& n,int p)
{
    if(p==1){
        for(int i=0;i<n;i++) scanf("%llu",&a[i]);
    }
    else{
        int mod; scanf("%llu%llu%d",&k1,&k2,&mod);
        for(int i=0;i<n;i++) a[i]=rng()%mod;
    }
    sort(a,a+n);
    int l=0;
    for(int i=0,j=0;i<n;i=j){
        while(j<n&&a[j]==a[i]) j++;
        T[l++]=P(a[i],j-i);
    }
    n=l;
}
bool cmp(const P& a, const P& b)
{
    return a.first+a.second>b.first+b.second;
}
int main()
{
    int T; scanf("%d",&T);
    while(T--){
        int n,m,p; scanf("%d%d%d",&n,&m,&p);
        init(A,n,p); init(B,m,p);
        int SA=0,SB=0,k=0;
        int p1=0,p2=0;
        while(p1<n||p2<m){
            if(p1<n&&p2<m&&A[p1].first==B[p2].first) C[k++]=P(A[p1++].second,B[p2++].second); //双方手里的相同颜色的牌的数目
            else if(p1==n||(p2<m&&A[p1].first>B[p2].first)) SB+=B[p2++].second; //如果B[p2].first比较小,那么说明不可能有相同颜色了
            else if(p2==m||(p1<n&&A[p1].first<B[p2].first)) SA+=A[p1++].second; //同上,所以直接加上贡献,这是互不影响的
        }
        sort(C,C+k,cmp); //按照贡献和排序
        for(int i=0;i<k;i++){
            if(i%2==0) SA+=C[i].first;
            else SB+=C[i].second;
        }
        if(SA>SB) puts("Cuber QQ"); //Cuber QQ 先手
        else puts("Quber CC");
    }
	return 0;
}