[2020多校联考]手套

[BalticOI 2008]手套

Description

有两个可重集 \(A\)\(B\),每个集合里有若干元素,每种元素有若干个。可以选择从 \(A\) 集中等概率随机选 \(x\) 个到 \(C 集\),从 \(B\) 中等概率随机选择 \(y\) 个到 \(D\),使得一定会使 \(C\)\(D\) 有交。最小化 \(x+y\),在 \(x+y\) 相等时最小化 \(x\)

Solution

因为要使得一定有交,所以考虑选了一定数目后,没有交的最坏情况。最坏情况显然是对于每种元素,其中一个集合中的该元素取完了,而另一个集合中的该元素一个都没有取到。而每种选择情况都会构成一个点 \((x,y)\),那么显然对所有 \(x'<x\)\(y' <y\) 的选择方案 \((x',y')\) 一定都不能构成合法方案,因为 \((x,y)\) 最坏。

考虑 \((x+1,y+1)\),因为 \((x,y)\) 最坏,再加入一个元素后,一定可以构成一组合法方案。所以只需要统计边界上的点,从而更新出合法的点的坐标即可。

显然棕色点比红色点优(\(x+y\) 小),所以用两个边界上的点更新一个合法的点。对于一组非法点 \((x_{i-1},y_{i-1})\)\((x_i,y_i)\) ,可以构造出一个合法点 \((x_{i-1}+1,y_i+1)\)

#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 21

struct Node{
    int x,y;
    bool operator <(const Node &X) const{
        return x==X.x? y>X.y:x<X.x;
    }
}a[N],s[1<<N],sta[1<<N];

int top,n;
int main(){
    freopen("gloves.in","r",stdin);
    freopen("gloves.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i].x);
    for(int i=1;i<=n;i++) scanf("%d",&a[i].y);
    int p=1<<n;
    for(int i=1;i<=p;i++)
        for(int j=0;j<n;j++)
            if((i>>j)&1) s[i].x+=a[j+1].x;
            else s[i].y+=a[j+1].y;
    sort(s+1,s+1+p);
    s[0].x=s[1].x+114514;
    for(int i=1;i<=p;i++){
        if(s[i].x==s[i-1].x) continue;
        while(top&&sta[top].y<=s[i].y) top--;
        sta[++top]=s[i];
    }
    int ans=(1<<30)+(1<<29)+(1<<28)+((114514-114513)<<27),x,y;
    for(int i=2;i<=top;i++){
        int ret=sta[i].y+sta[i-1].x+2;
        if(ret<ans) ans=ret,x=sta[i-1].x+1,y=sta[i].y+1;
    }
    printf("%d\n%d",x,y);
}
/*
4
0 7 1 6
1 5 0 6
*/
posted @ 2020-11-30 21:47  Kreap  阅读(95)  评论(0编辑  收藏  举报