牛客OI周赛15-普及组部分题解

链接:https://ac.nowcoder.com/acm/contest/4911

B.三角形

题目大意:

给定n个盒子,每个盒子中有一定数量的宝物,现在你每次可以从一个盒子中拿取一个宝物,这样就会有很多种不同的价值和结果。现在问你前k小的价值结果的和是多少?

n<=100,k<=10000,假设盒子中最多宝物数量为m,保证k<=n*m<=10000。宝物价值不超过100.

思路:

首先这个题容易被数据范围误导,虽然每个盒子中最多可能有m<=100个物品,但是你最多从盒子中拿走一个物品,也就是说最后的结果sum<=n*100的,最大就是10000。这样分析一下数据范围,就可以想到方案数背包了(因为k最多也就10000),dp(i,sum)表示前i个盒子中拿物品,和为sum的方案数,这样复杂度为O(n * m * sum),最大为1e8左右。

#include 
#include 
#include 
#include 
#include 
#include 
#define int long long 
using namespace std;

const int maxn=10000+10;
int dp[101][maxn];
int mp[101][101];
signed main(){
    int n,m;
    scanf("%lld%lld",&n,&m);
    int sum=0;
    for(int i=1;i<=n;i++){
        scanf("%lld",&mp[i][0]);
        int maxx=0;
        for(int j=1;j<=mp[i][0];j++){
            scanf("%lld",&mp[i][j]);
            maxx=max(maxx,mp[i][j]);
        }
        sum+=maxx;
    }
    //printf("sum %lld\n",sum);
    dp[0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=mp[i][0];j++){
            for(int k=sum;k>=mp[i][j];k--){
                if(dp[i-1][k-mp[i][j]]){
                    dp[i][k]+=dp[i-1][k-mp[i][j]];
                }
            }
        }
    }
    int cnt=0;
    int res=0;
    //printf("debug %lld\n",dp[n][1]);
    for(int i=1;i<=sum;i++){
        while(dp[n][i]){
            cnt++;
            res+=i;
            dp[n][i]--;
            if(cnt==m){
                return printf("%lld\n",res),0;
            }
        }
    }
    return 0;
}

D:多元组

题目大意:

定义一个k元组为

现在给定一个数列,问你数列中最多有多少个k元组?对1e9+7取模

n<=1e5,k<=50.

思路:

首先思考一个经典的问题:逆序对问题。逆序对问题是k=2时的特殊情况,当n<=1e5时逆序对可以用树状数组快速求解。那么我们如何从二元组推三元组的情况呢?对于一个数a[i],他能组成三元组的情况,一定可以从在他之前,且值比他小的二元组的情况推导过来。

所以,令dp(i,j)表示前i个元素中,组成j元组的情况的个数,那么递推式就很明显了

dp[i][j]=\sum_{k=1}^{i-1}dp[k][j-1] *(a[k]<a[i])

递推式出来了,但是很明显直接算是O(n^2)的复杂度,所以我们需要借助树状数组快速计算。

具体来说,为每个元组k,开一颗树状数组,最多也就50颗树状数组这样,然后我们计算完dp(i,j)之后将其插入到对应的树状数组c(a[i],j)这个位置即可。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define int long long
using namespace std;

const int maxn=1e5+10;
int c[maxn][51];
int f[maxn][51];
const int mod=1e9+7;

int a[maxn];
int lowbit(int x){
	return x&-x;
}
vectorv;
int n,m;
int getid(int cur){
	return lower_bound(v.begin(),v.end(),cur)-v.begin()+1;
}
void add(int x,int v,int y){
    while(x<=n){
        c[x][y]+=v;
        x+=lowbit(x);
    }
}
int query(int x,int y){
    int res=0;
    while(x>0){
        (res+=c[x][y])%=mod;
        x-=lowbit(x);
    }
    return res;
}

signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());

    for(int i=1;i<=n;i++){
        a[i]=getid(a[i]);
    }
    for(int i=1;i<=n;i++){
        add(a[i],1,1);
        for(int j=2;j<=m;j++){
            f[i][j]+=query(a[i]-1,j-1);
            f[i][j]%=mod;
        }
        for(int j=2;j<=m;j++){
            add(a[i],f[i][j],j);
        }
    }
    printf("%lld\n",query(n,m));
    return 0;
}

 

你可能感兴趣的:(补题)