3512. 【NOIP2013模拟11.5A组】游戏节目(show)

Description

有三支队伍,分别是A,B,C。有n个游戏节目,玩第i个游戏,队伍A可以得到的分数是A[i],队伍B可以得到的分数是B[i],队伍C可以得到的分数是C[i]。由于时间有限,可能不是每个节目都能玩,于是节目主持人决定要从n个游戏节目里面挑选至少k个节目出来(被选中的节目不分次序),使得队伍A成为赢家。队伍A能成为赢家的条件是队伍A的总得分要比队伍B的总得分要高,同时也要比队伍C的总得分要高。节目主持人有多少种不同的选取方案?

Input

第一行,两个整数n和k。

第二行, n个整数,分别是A[1]、A[2]、A[3]…A[n]。

第三行, n个整数,分别是B[1]、B[2]、B[3]…B[n]。

第四行, n个整数,分别是C[1]、C[2]、C[3]…C[n]。

Output

一个整数,表示不同的选取方案数量。

Sample Input

3 2

1 1 2

1 1 1

1 1 1

Sample Output

3

【样例解释】

方案一:选取节目1和节目3。

方案二:选取节目2和节目3。

方案三:选取节目1、节目2、节目3。

Data Constraint

对于40%数据,2 <= n <= 20。

对于100%数据,2 <= n <= 34, 1 <= k <= min(n,7), 1 <=A[i], B[i], C[i]<= 10^9。

Solution

分两步处理:
第一步:把问题简单化,假设没有k的限制,设求出来的方案总数是x。
第二步:考虑k的限制,由于k<7,可以穷举n个节目取0个,n个节目取1个,n个节目取2个,n个节目取3个,n个节目取3个,n个节目取4个,n个节目取5个,n个节目取6个,穷举完这几种情况就可以知道哪些方案是合法的。而且Combinations(34,0) + Combinations(34,1) + Combinations(34,2) + Combinations(34,3) + Combinations(34,4) + Combinations(34,5) + Combinations(34,6) = 1676116。
也就是这个穷举不超过1676116次。设第二步的方案总数是y。

那么,最后应该输出的答案就是x - y。

第二步的方案数y可以搜索计算结果,下面重点讲讲第一步的方案数x如何求。
由于n最大是34,直接搜索会超时。可以把n个节目平均分成两部分,即第1至第n/2个节目归为第1部分,第n/2+1个节目至第n个节目归为第2部分。

第1部分:显然最多只有17个节目,每个节目可以取或者不取,穷举这17个节目的所有情况,显然有2^17种取法,对于每一种取法,队伍A都有一个得分,设为scoreA, 队伍B也有一个得分scoreB,队伍C也有一个得分scoreC。不妨设difAB1 = scoreA - scoreB, difAC1 = scoreA - scoreC,即每一种取法,都可以计算出一对值(difAB1,difAC1),

第2部分:显然最多也只有17个节目。每个节目可以取或者不取,穷举这17个节目的所有情况,显然也是有2^17种取法。同理,对于每一种取法,设difAB1 = scoreA - scoreB, difAC1 = scoreA - scoreC,即每一种取法都可以计算出一对值(difAB2,difAC2),

显然,如果一个方案要成立,必须要同时满足:
difAB1 + difAB2 > 0 (即队伍A的总得分比队伍B的总得分高)
difAC1 + difAC2 > 0 (即队伍A的总得分比队伍C的总得分高)

于是,问题转化为,枚举一对值(difAB1,difAC1),在第2部分里面查询有多少对(difAB2,difAC2),使得同时满足
difAB1 + difAB2 > 0
difAC1 + difAC2 > 0

显然,对于第2部分,可以用树状数组或者线段树之类的数据结构进行保存,以备第1部分的查询所需。

这里主要讲一下权值线段树的实现方法:
首先我们暴力求出来了四AB个数组,分别是:difAB1[],difAB2[],difAC1[]和difAC2[]
我们将difAB1从大到小排序,同是difAC1也跟着变,再将difAB2从小到大排序,difAC2跟着。
接着我们枚举difAB1,然后用一个j指针在difAB2中不断往后指,直到difAB1[i]+difAB2[j]>0,(因为difAB2是从小到大有序的,所以j指针不用更新)这个时候我们就满足difAB1 + difAB2 > 0了,接下来只要在j~len(difAC2)中找到difAC1[i]+difAC2[j]>0的总数就可以了,那么怎么找呢?这个时候就用到了权值线段树了,首先我们将difAC2中所有元素加入一颗权值线段树中,然后j指针每往后移动一位,就将difAC2[j]在权值线段树中删除(因为你要查询的区间右端点是固定的,既len(difAC2)),做完这些以后,就可以直接查询在j~len(difAC2)中有多少difAC1[i]+difAC2[j]>0的了,直接用区间查询,这个会吧。

由于分两步求答案,于是时间复杂度 = x + y = 2^17 * Log(2^17) + 1676116

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define N 50
#define M 1000010
using namespace std;
ll MAX=4000000009LL;
ll ans=0;
int n,k;
int a[35],b[35],c[35],ss=0,ls[M*3],rs[M*3],tree[M*3],tot=0;
ll f1[131073],f2[131073],g1[131073],g2[131073];
void dg(int x,int t,ll sab,ll sac){
    if(t>=k) return;
    if(x>n){
        if(sab>0&&sac>0) tot++;
        return;
    }
    dg(x+1,t+1,sab+a[x]-b[x],sac+a[x]-c[x]);
    dg(x+1,t,sab,sac);
}
void dfs1(int x,ll sab,ll sac){
    if(x>n/2){
        f1[++f1[0]]=sab;
        f2[++f2[0]]=sac;
        return;
    }
    dfs1(x+1,sab+a[x]-b[x],sac+a[x]-c[x]);
    dfs1(x+1,sab,sac);
}
void dfs2(int x,ll sab,ll sac){
    if(x>n){
        g1[++g1[0]]=sab;
        g2[++g2[0]]=sac;
        return;
    }
    dfs2(x+1,sab+a[x]-b[x],sac+a[x]-c[x]);
    dfs2(x+1,sab,sac);
}
void qs1(int l,int r){
    int i=l,j=r;
    ll m=f1[(l+r)/2];
    while(i<=j){
        while(f1[i]>m) i++;
        while(f1[j]<m) j--;
        if(i<=j){
            swap(f1[i],f1[j]);
            swap(f2[i],f2[j]);
            i++,j--;
        }
    }
    if(l<j) qs1(l,j);
    if(i<r) qs1(i,r);
}
void qs2(int l,int r){
    int i=l,j=r;
    ll m=g1[(l+r)/2];
    while(i<=j){
        while(g1[i]<m) i++;
        while(g1[j]>m) j--;
        if(i<=j){
            swap(g1[i],g1[j]);
            swap(g2[i],g2[j]);
            i++,j--;
        }
    }
    if(l<j) qs2(l,j);
    if(i<r) qs2(i,r);
}
void insert(int x,ll l,ll r,ll k,int p){
    tree[x]+=p;
    if(l>=r) return;
    ll mid=(l+r)>>1;
    if(k<=mid){
        if(!ls[x]) ls[x]=++ss;
        insert(ls[x],l,mid,k,p);
        if(!tree[ls[x]]) ls[x]=0;
    }
    else{
        if(!rs[x]) rs[x]=++ss;
        insert(rs[x],mid+1,r,k,p);
        if(!tree[rs[x]]) rs[x]=0;
    }
}
int find(int x,ll l,ll r,ll k){
    ll t=0;
    if(l==r) return tree[x]; 
    ll mid=(l+r)/2;
    if(k<=mid){
        if(ls[x]) t=find(ls[x],l,mid,k);
    }
    else{
        if(ls[x]) t=tree[ls[x]];
        if(rs[x]) t+=find(rs[x],mid+1,r,k);
    }
    return t;
}
int main(){
    freopen("show.in","r",stdin);
    freopen("show.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    dg(1,0,0,0);
    dfs1(1,0,0);
    dfs2(n/2+1,0,0);
    qs1(1,f1[0]);
    qs2(1,g1[0]);
    for(int i=1;i<=g2[0];i++) insert(0,0,MAX*2,-g2[i]+MAX,1);
    int j=1;
    for(int i=1;i<=f1[0];i++){
        while(f1[i]+g1[j]<=0&&j<g2[0]){
            insert(0,0,MAX*2,-g2[j]+MAX,-1);
            j++;
        }
        if(f1[i]+g1[j]>0) ans+=find(0,0,MAX*2,f2[i]+MAX-1); 
    }
    printf("%lld",ans-tot);
    return 0;
}

作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/81771765

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/81771765