51NOD 1821 最优集合 [并查集]

传送门

题意:

一个集合S的优美值定义为:最大的x,满足对于任意i∈[1,x],都存在一个S的子集S',使得S'中元素之和为i。

给定n个集合,对于每一次询问,指定一个集合S1和一个集合S2,以及一个数k,要求选择一个S2的子集S3(|S3|<=k),使得S1∪S3的优美值最大。(集合元素可以重复)
$n,m \le 1000, Q \le 1000$

比赛时看了一眼题没认真想其实不难....现在想出来(也晚了)
考虑如何求最优值
如果现在最优值为$now$,只要找到下一个未用的$\le now+1$的最小元素$x$,最优值就可以变成$now+x$
如果找不到怎么办?去$S2$里找$\le now+1$的未用的最大元素
实现上可以用一个并查集记录某一个元素的上一个(包括自己)未用元素是谁做到$\alpha(m)$
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1005;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int n,a,b,k;
struct Set{
    int s[N],m;
    int& operator [](int x){return s[x];}
    void Sort(){sort(s+1,s+1+m);}
}s[N];
int fa[N];
inline int find(int x){return x==fa[x] ? x : fa[x]=find(fa[x]);}
void solve(Set &a,Set &b,int k){
    //printf("%d  ",a.m);for(int i=1;i<=a.m;i++) printf("%d ",a[i]);puts("");
    //printf("%d  ",b.m);for(int i=1;i<=b.m;i++) printf("%d ",b[i]);puts("");
    int n=a.m , m=b.m;
    for(int i=1;i<=m;i++) fa[i]=i;
    int now=0,p1=1,p2=1;
    while(true){//printf("now %d %d %d\n",now,p1,p2);
        if(p1<=n && a[p1]<=now+1) now+=a[p1++];
        else if(k){ k--;
            while(p2<=m && b[p2]<=now+1) p2++;
            p2--;
            if(fa[p2]==p2) fa[p2]=find(p2-1),now+=b[p2++];
            else{
                int x=find(p2);//printf("x %d\n",x);
                if(x) fa[x]=find(x-1),now+=b[x];
                else break;
            }
        }else break;
    }
    printf("%d\n",now);
}
int main(){
    freopen("in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++){
        s[i].m=read();
        for(int j=1;j<=s[i].m;j++) s[i][j]=read();
        s[i].Sort();
    }
    int Q=read();
    while(Q--){
        a=read();b=read();k=read();
        solve(s[a],s[b],k);
    }
}

 

 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值