Balanced Diet(前缀数组)

题目链接:codeforces.com/gym/102220/problem/B

Balanced Diet

Taylor is wandering in a milk candy store. The store has m types of sweets and there are n sweets in the store. The i-th sweet has the value of ai, and it is of type bi.

Taylor is planning to buy some sweets in the store, each sweet can be bought at most once. He will buy at least one sweet. Taylor knows that a balanced diet is important, the value of a sweet set is measured as S/C, where S denotes the sum of ai and C denotes the maximum number of occurrences among all types of sweets.

Assume Taylor selects pi sweets of type i, it is not welcomed if 1≤pi<li. Note that pi can also be 0 and pi can be everything when li=1.

Please write a program to help Taylor find the sweet set with maximum value.

Input
The first line of the input contains an integer T(1≤T≤1000), denoting the number of test cases.
In each test case, there are two integers n,m(1≤n,m≤100000) in the first line, denoting the number of sweets and types.
In the second line, there are m integers l1,l2,…,lm(1≤li≤n).
For the next n lines, each line contains two integers ai,bi(1≤ai≤10e8,1≤bi≤m), denoting each sweet.
It is guaranteed that ∑n≤10e6
and ∑m≤10e6, and there always exists a valid sweet set.

Output
For each test case, print a single line of format u/v, denoting the maximum value u/v. Note that you should guarantee that gcd(u,v)=1.

Example

Input
2
2 1
2
7 1
2 1
3 2
1 2
2 1
5 2
3 2

Output
9/2
5/1

题意:给出共m种类型的n颗糖果,每颗糖果有自己的价值和属于一种类型,让你选出一些糖果(每类糖果要么不选,要么至少选li个),求最大的S/C,S是选出糖果的价值总和,C是选出糖果构成所有类型中个数最大值,比如你选了1类糖果3个,2类糖果6个,3类糖果2个,这时C为6。

思路:枚举分母C,C只能是li。用vector数组来保存每类糖果,在每类糖果中选一定数目时,价值越大越好,所以对每个vector排降序,排序后将vector做成前缀和数组,再将每类糖果按li排序,当我们枚举li时,前面的lk<=li,前面的糖果可取,后面的lj>li,不可取。分析题目的数据量,不同的li最多有n^(1/2)个,li:1,2,3,4,5…,所有的li加起来小于等于n。

例如:
10 9 8 7 6 6 5 4 3
12 8 7 6 4 2 2 1 1 1
12 8 7 6 4 2 2 1 1 1
13 8 7 6 4 3 2 1 1 1 1 1 1
一行代表一类糖的价值,加粗代表li,按li排好序,2<6<6<10。

枚举分母C:
C取2、3、4、5
C取6、7、8、9
C取10、11、12、13

可以发现:
C取2大于或等于取3、4、5
C取6大于或等于取7、8、9
C取10大于或等于取11、12、13
对于一个递减的数列a1、a2、a3…,前i个数的平均大于等于前j个数的平均(i<j)。当要从一类糖中取的个数大于该类糖的个数时,假设有一些价值为0的糖存在即可。

实现代码:

#include <iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
using namespace std;

typedef long long ll;

const int INF=0x3f3f3f3f;
const int MAX_N=100000+5;

int n,m;
vector<ll> sum[MAX_N+1];//,每类糖的前缀和数组
int L[MAX_N+1];
int rak[MAX_N+1];//间接排序

bool cmp(int x,int y){
    return x>y;
}

bool cmp2(int i,int j){
    return L[i]<L[j];
}

ll gcd(ll a,ll b){
    if(b==0)return a;
    return gcd(b,a%b);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)scanf("%d",&L[i]);
        int a,b;
        for(int i=1;i<=m;i++){
            sum[i].push_back(0);
        }
        for(int i=1;i<=n;i++){
            scanf("%d%d",&a,&b);
            sum[b].push_back(a);
        }
        
        for(int i=1;i<=m;i++){
            sort(sum[i].begin()+1,sum[i].end(),cmp);
            for(int j=1;j<sum[i].size();j++){//构造前缀和
                sum[i][j]+=sum[i][j-1];
            }
        }
        
        for(int i=1;i<=m;i++)rak[i]=i;
        sort(rak+1,rak+m+1,cmp2);//按L排序
        
        ll A=0,B=1;
        for(int ii=1;ii<=m;ii++){
            int si=L[rak[ii]];
            while(ii<=m&&L[rak[ii]]==si)ii++;//取不同的L枚举
            ii--;
            int i=rak[ii];
            ll ans=0;
            for(int jj=1;jj<=ii;jj++){
                int j=rak[jj];
                int k=min(L[i],(int)sum[j].size()-1);
                ans+=sum[j][k];
            }
            if(ans*B>A*L[i]){
                A=ans;B=L[i];
            }
        }
        
        ll gc=gcd(A,B);
        A/=gc;B/=gc;
        printf("%lld/%lld\n",A,B);
        
        for(int i=1;i<=m;i++){
            sum[i].clear();
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值