尽梨了
- problem
- solution
- code
problem
丰之崎学园附近共有 nnn 个商店,在时刻 000 时,英梨梨从学园出发开始购物。
从学园走到任意一个商店,或从一个商店走到另一个商店需要 111 单位时间。
如果英梨梨在时刻 ttt 到达了商店 iii ,她需要先花费 ai×t+bia_i × t + b_iai×t+bi 的时间排队,
然后才能购买这家商店的物品。
英梨梨想知道,在时刻 T+0.5T + 0.5T+0.5 之前她最多能在多少个不同商店买到物品呢?
这里假定只会在走路和排队上消耗时间,购买物品不消耗时间。
n≤2e5,T≤1e9n\le 2e5,T\le 1e9n≤2e5,T≤1e9。
solution
假设在 ttt 时刻选择 iii 商店购买,然后立即到 jjj 商店购买,则 jjj 商店会多付出 (ai⋅t+bi+1)⋅aj(a_i·t+b_i+1)·a_j(ai⋅t+bi+1)⋅aj 的时间。
假设在 ttt 时刻选择 jjj 商店购买,然后立即到 iii 商店购买,则 iii 商店会多付出 (aj⋅t+bj+1)⋅ai(a_j·t+b_j+1)·a_i(aj⋅t+bj+1)⋅ai 的时间。
若选择先 iii 后 jjj 的顺序,则需满足 (ai⋅t+bi+1)⋅aj<(aj⋅t+bj+1)⋅ai⇒(bi+1)⋅aj<(bj+1)⋅ai(a_i·t+b_i+1)·a_j<(a_j·t+b_j+1)·a_i\Rightarrow (b_i+1)·a_j<(b_j+1)·a_i(ai⋅t+bi+1)⋅aj<(aj⋅t+bj+1)⋅ai⇒(bi+1)⋅aj<(bj+1)⋅ai。
发现商店的先后顺序与时间 ttt 无关。
所以可以按照这个关系进行商店排序。
最后实际去的商店顺序按照时间排序肯定是按这个关系排序后形成的新序列的一个子序列。
暴力地设 dpi,j:dp_{i,j}:dpi,j: 在遍历到 iii 商店(已经排序过),在 jjj 个商店买到物品的最少花费时间,转移考虑是否进入 iii 商店即可,时间复杂度 O(n2)O(n^2)O(n2)。
考虑特殊数据点 ∀iai>0\forall_i\ a_i>0∀i ai>0。
通过时间转移公式可以发现,如果 ai≠0a_i\neq 0ai=0,那么进入商店后的时间是指数级增长的。
这就意味着转移的第二位只需要开到 logT\log TlogT 级别就可以了。
实际上,这已经几乎是正解了。
将所有 ai=0a_i=0ai=0 的商店排到最后,只考虑 ai≠0a_i\neq 0ai=0 的商店,按照上面优化后的 dpdpdp 做即可。
最后考虑剩余的时间 T−dpk,iT-dp_{k,i}T−dpk,i 后面能接多少个 ai=0a_i=0ai=0 的商店,贪心的将这些特殊商店按照 bbb 升序排序。
时间复杂度:O(nlogn+nlogT)O(n\log n+n\log T)O(nlogn+nlogT)。
code
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define maxn 200005
struct node { int a, b; }it[maxn];
int n, m, T, k;
int dp[maxn][35];void read( int &x ) {x = 0; char s = getchar();while( s < '0' or s > '9' ) s = getchar();while( '0' <= s and s <= '9' ) x = ( x << 1 ) + ( x << 3 ) + ( s ^ 48 ), s = getchar();
}signed main() {freopen( "eriri.in", "r", stdin );freopen( "eriri.out", "w", stdout );read( n ), read( T );m = log( T ) / log( 2 );for( int i = 1;i <= n;i ++ ) read( it[i].a ), read( it[i].b );sort( it + 1, it + n + 1, []( node x, node y ) { return ( x.b + 1 ) * y.a < ( y.b + 1 ) * x.a; } );while( it[k + 1].a ) k ++;for( int i = 0;i <= n;i ++ )for( int j = 1;j <= m;j ++ ) dp[i][j] = T + 1;for( int i = 1;i <= k;i ++ )for( int j = 1;j <= m;j ++ )dp[i][j] = min( min( T + 1, dp[i - 1][j] ), dp[i - 1][j - 1] + 1 + ( dp[i - 1][j - 1] + 1 ) * it[i].a + it[i].b );sort( it + k + 1, it + n + 1, []( node x, node y ) { return x.b < y.b; } );int ans = 0;for( int i = 0;i <= m and dp[k][i] <= T;i ++ ) {int sum = T - dp[k][i], cnt = 0;for( int j = k + 1;j <= n and sum > it[j].b;j ++ )cnt ++, sum -= it[j].b + 1;ans = max( ans, i + cnt );}printf( "%lld\n", ans );return 0;
}