Task

Task

给出大小为n的集合(S_1),其中第i个元素为((a_i,b_i)),给出大小为m的集合(S_2),第i个元素为((a_i,b_i)),现在要从集合(S_2)中选出元素i与集合(S_1)的元素j配对,但是要满足(a_ileq a_j),(b_ileq b_j),然后得到权值(a_i imes 500+b_i imes 2),询问满足配对数量尽可能多的情况下,最大的权值之和,(1 <= N <= 100000,1 <= M <= 100000),(a_ileq 1440,b_ileq 100)

注意到这个问题考虑起来过于麻烦,于是需要转换模型,考虑转换为数列模型,不妨将两个集合合并成一个集合,并标记好属于哪个集合,属于(S_1)标记为1,否则标记为0,然后统一按照(b_i)为关键字从小到大排序,如果(b_i)相同则按起被标记的号子大小排序(含义就是属于(S_1)要在后面,往后看,你就知道为什么了),记新的数列中第i个元素为((a_i,b_i,l_i))(其中(l_i)对应其被标记的数字)。

先考虑如何选得多。

注意到此时对于其中一个(l_i=1)的元素,因为b已经从小到大排序,能够与之配对的元素j必然是它的前面的(l_j=0),且(a_jleq a_i)的元素,不妨暴力找出这些元素,现在问题是如何选择这些元素与i配对,朴素的感觉告诉我们最大的满足条件的(a_j)

首先证明(i)能够选择一个元素与之配对,否则让出前面的元素给后面的不会配对的元素的配对,结果不改变,如果是让后面的一个会配对的元素配对,放弃它的配对的元素,从而可以让一个新的元素能够配对,也不会改变结果,于是i一定可以选择。

然后对于i的选择,对于可以选择(j,k(a_k<a_j)),必然对于后面的元素,要么都不能选择,显然此时i选择一个元素是优秀的,要么是k能够选择,j不能选择,i选择了是优秀的,或者是都能选择,此时i选择任意一个都可以,于是选择(j)一定优秀。

现在问题是如何查询小于(a_i)的最大的(a_j),决策集合有增减,显然想到维护单调性,于是一棵平衡树就可以支持查询这个元素(显然用set替代),并且支持快速插入和删除,于是我们可以做到选的多。

现在问题在于如何使结果尽可能大,注意到一个性质,(a)带来的价值远大于(b),也就是一个b能够带来最大的权值为(200),而a增加一个数字,就能增加(500),于是只要让a尽可能优秀,结果就会更加优秀了,因此我们寻找尽可能大的(a_j)的策略保证了a最大,是非常优秀的。

还存在一个问题,也就是如果(a_i)相同如何选择,显然选择(b_i)较大的,这个也不难,只要在平衡树再加一个关键字就是按照(b)排序,注意此时查询时,对应的b要赋值为无穷大(不理解看代码)。

因此我们就可以(O(nlog(n)))解决这个问题,事实上你可以不用平衡树,因为(a_i)很小,可以桶排查找这个最大值,于是你可以改成在(O(na_i))解决这个问题,理论时间复杂度很高,但常数很小。

参考代码:

#include <iostream>
#include <cstdio>
#include <set>
#include <cstring>
#include <algorithm>
#define il inline
#define ri register
#define ll long long
#define Size 250000
#define intmax 0x7fffffff
using namespace std;
struct san{
    int x,y,l;
    il bool operator<(const san&a)const{
        return x==a.x?y<a.y:x<a.x;
    }
}I[Size];
multiset<san>S;
multiset<san>::iterator gzy;
il void read(int&);
il bool comp(const san&,const san&);
int main(){int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){S.clear(),memset(I,0,sizeof(I));
        for(int i(1);i<=n;++i)read(I[i].x),read(I[i].y),I[i].l=1;
        m+=n;for(int i(n+1);i<=m;++i)read(I[i].x),read(I[i].y);
        sort(I+1,I+m+1,comp);int tot(0);ll ans(0);
        for(int i(1);i<=m;++i){
            if(I[i].l){
                if(S.empty())continue;
                I[i].y=intmax,gzy=S.upper_bound(I[i]);
                if(gzy==S.begin())continue;
                --gzy,++tot,ans+=gzy->x*500+gzy->y*2;
                S.erase(gzy);
            }else S.insert(I[i]);
        }printf("%d %lld
",tot,ans);
    }
    return 0;
}
il bool comp(const san&a,const san&b){
    return a.y==b.y?a.l<b.l:a.y<b.y;
}
il void read(int &x){
    x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
原文地址:https://www.cnblogs.com/a1b3c7d9/p/11234277.html