[CF808]Selling Souvenirs
题目
题解
这道题有两种做法,前者 \(\mathcal O(n)\),后者 \(\mathcal O(n\log_{\frac{3}{2}}n)\) .(此处将 \(n,m\) 视作同阶,不作明显区分)
首先定义 \(a[i][j]\) 为重量为 \(i\) 而价格在重量为 \(i\) 的第 \(j\) 贵的物品.
方法一 —— DP
令 \(f[i]\) 表示在空间为 \(i\) 时,只选重量为 \(1\) 和 \(2\) 的商品的最大价值,那么我们有初始化 \(f[1]=a[1][1]\)
而后,我们可以每 \(2\) 的跨度为一个单位,每次考虑是选一个 \(2\) 物品还是两个 \(1\) 物品,进行比较之后更新 \(f\) 数组即可
在得到 \(f\) 数组之后,我们用 \(\mathcal O(\frac{m}{3})\) 的复杂度暴力枚举 \(3\) 物品选了多少个,用 \(\mathcal O(1)\) 即可算出价值
方法二 —— 三分查找
同样的思路,首先暴力枚举选了多少个 \(3\) 物品,然后,在重量固定时,以 \(2\) 物品的数量为 \(x\),定义 \(f(x)\) 为价值,可以证明 \(f(x)\) 是单峰的
在 \(x\) 越大时,选入的 \(2\) 物品价值会越来越小,而剩下的空间来选 \(1\) 物品,一定是尽量往大选,所以,当 \(f(x)\) 下降时,表示再加入的一个 \(2\) 物品价值已经少于扔掉的俩 \(1\) 物品的价格,而扔掉的 \(1\) 物品会随 \(x\) 增加而越来越大,而加入的 \(2\) 物品价值会随 \(x\) 增加而越来越小,所以,当 \(f(x)\) 开始下降之后,就没有可能再增加,即可证明 \(f(x)\) 是单峰的,所以可以使用三分
那么,外圈枚举 \(3\) 物品,对于每一个 \(i\) 做三分,最后标记最大价值即可.
代码
方法一
#include<cstdio>
#include<algorithm>
using namespace std;
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void read(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T read(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int MAXN=100000;
const int MAXM=300000;
int n,m;
int c[4][MAXM+5],sz[4];
LL pre[MAXM+5];//计算重量为 3 的商品的价值前缀和
inline bool cmp(const int x,const int y){return x>y;}
inline void Input(){
n=read(1),m=read(1);
int w;
rep(i,1,n){w=read(1);
c[w][++sz[w]]=read(1);
}
rep(i,1,3){
sort(c[i]+1,c[i]+sz[i]+1,cmp);
sz[i]=m;//将少的东西用价值为 0 的补足
}
rep(i,1,sz[3])pre[i]=pre[i-1]+c[3][i];
}
LL f[MAXM+5];//在 w=i 时只选重量为 1,2 的商品, 能达到的最大价值为多少
inline void Solvef(){
f[1]=c[1][1];
int pos[3][3]={{0},{0,1,1},{0,2,1}};
int s;
rep(i,2,m){
s=(i&1)+1,f[i]=f[i-2];
if(c[1][pos[s][1]]+c[1][pos[s][1]+1]>c[2][pos[s][2]]){
f[i]+=0ll+c[1][pos[s][1]]+c[1][pos[s][1]+1];
pos[s][1]+=2;
}else{
f[i]+=0ll+c[2][pos[s][2]];
++pos[s][2];
}
}
}
inline LL calc(const int x){return pre[x]+f[m-3*x];}
inline void Solve(){
int l=0,r=Min(sz[3],m/3),p1,p2;
LL ans=0;
rep(i,0,Min(sz[3],m/3))ans=Max(ans,calc(i));
writc(ans,'\n');
}
signed main(){
Input();
Solvef();
Solve();
return 0;
}
/*
------------------------
6 12
1 10
3 11
3 11
3 11
3 11
3 11
ans == 44
------------------------
6 13
2 18
2 18
2 18
2 18
3 22
3 22
ans == 98
-------------------------
6 9
2 20
2 20
2 20
2 20
3 21
3 21
ans == 81
-------------------------
*/
方法二
#include<cstdio>
#include<algorithm>
using namespace std;
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void read(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T read(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int MAXN=100000;
const int MAXM=300000;
int n,m;
int c[4][MAXM+5],sz[4];
LL pre[4][MAXM+5];//计算重量为 3 的商品的价值前缀和
inline bool cmp(const int x,const int y){return x>y;}
inline void Input(){
n=read(1),m=read(1);
int w;
rep(i,1,n){w=read(1);
c[w][++sz[w]]=read(1);
}
rep(i,1,3){
sort(c[i]+1,c[i]+sz[i]+1,cmp);
sz[i]=m;//将少的东西用价值为 0 的补足
}
rep(i,1,3)rep(j,1,sz[3])pre[i][j]=pre[i][j-1]+c[i][j];
}
inline LL calc(const int w,const int x){
return pre[1][w-x*2]+pre[2][x];
}
inline LL Trisearch(const int w){
int l=0,r=Min(sz[2],w/2),p1,p2;
while(l+2<r){
p1=l+(r-l+1)/3,p2=r-(r-l+1)/3;
if(calc(w,p1)>=calc(w,p2))r=p2;
else l=p1;
}
LL ret=0;
rep(i,l,r)ret=Max(ret,calc(w,i));
return ret;
}
inline void Solve(){
LL ans=0;
rep(i,0,Min(sz[3],m/3))ans=Max(ans,pre[3][i]+Trisearch(m-3*i));
writc(ans,'\n');
}
signed main(){
Input();
Solve();
return 0;
}
/*
------------------------
6 12
1 10
3 11
3 11
3 11
3 11
3 11
ans == 44
------------------------
6 13
2 18
2 18
2 18
2 18
3 22
3 22
ans == 98
-------------------------
6 9
2 20
2 20
2 20
2 20
3 21
3 21
ans == 81
-------------------------
*/