看错时间导致错过一场比赛......
题意,有m种无限多的物品,有两个价值(a[i],b[i]),若选x[i](x[i] >= 1)个,则能获得(a[i] + (x[i] - 1) * b[i])的价值(若不选则价值为0)
求选出n个,使得价值最大.m<=1e5,n,a[i],b[i]<=1e9;
分析:这个数据范围大概就是要贪心了,然后考虑怎么贪.我们发现,若只选一个,实质上只有a[i]在起作用,只有选两个以上,b[i]才有影响.可以猜测出要么所有的都只选一个,要么只有一种物品选超过一个.
证明如下:
设第i个物品选的个数为cnt[i],对于任意的i,j,若cnt[i],cnt[j]>=1,设b[i] >= b[j],则显然如果接下来还有若干个选择机会,选第i个物品一定比选j个物品优,因为既然都选了一次后,价值就与a[i]无关了,所以选择的贡献b[i] >= b[j].
所以我们可以枚举哪种物品要选超过一次.显然我们如果枚举到第i个物品,可以先钦定x[i] = n,即n个物品全选x[i],然后对于(j != i && a[j] > b[i]),再反悔一次,选择a[j],这个过程可以先排序,然后二分大于b[i]的a[j],前缀和加一下即可
记得特判所有物品都只选一种的情况
时间复杂度(nlogn)
(挺简单一道题,不知道为什么只有1000来个人过)
/*C. Choosing flowers*/ #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int maxn = 1e6 + 10; #define ll long long struct Num{ int fir,sec,id; bool operator < (Num A){ return fir < A.fir; } }a[maxn]; ll sum[maxn]; int n,m; int read(){ char ch = getchar(); int x = 0; while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = x * 10 + ch - 48,ch = getchar(); return x; } int get(int x){ int l = 1,r = m; int ans = 0; while(l <= r){ int mid = (l + r) >> 1; if(a[mid].fir <= x){ l = mid + 1; ans = mid; } else{ r = mid - 1; } } return m - ans; } int main() { int t = read(); while(t--){ n = read(),m = read(); for(int i = 1; i <= m; ++i){ a[i].fir = read(); a[i].sec = read(); a[i].id = i; } sort(a+1,a+m+1); for(int i = 1; i <= m; ++i) sum[i] = sum[i-1] + a[i].fir; ll ans = 0; if(n <= m){ ans = sum[m] - sum[m - n]; } for(int i = 1; i <= m; ++i){ int k = get(a[i].sec); ll res = sum[m] - sum[m - k]; if(a[i].fir <= a[i].sec){ k++; res += a[i].fir; } if(k >= n) continue; else{ res += 1ll * (n - k) * a[i].sec; ans = max(ans,res); } } printf("%lld ",ans); } return 0; }