题解 尽梨了
考场推式子的时候错了一个小细节导致整体爆炸
先考虑临项扰动,则 \(i\) 在 \(j\) 前仅当这样走完两者的总时间更小
即
\[a_it+b_i+(\color{red}{t}+a_it+b_i+\color{red}{1})a_j+b_j \leqslant a_jt+b_j+(\color{red}{t}+a_jt+b_j+\color{red}{1})a_a+b_a
\]
考场上就是忽略了上面这两个地方
然后我们得到了一个序列,但发现最优解不一定是在这个序列上贪心选
举个例子:\(i\) 在 \(j\) 前但剩下的时间只来得及走完 \(j\) 而走不完 \(i\)
重新想下我们刚才对“更优”的定义就会发现,原定义是在 \(i, j\) 都被选择的情况下,先选某一个更优
于是在整个序列都被选完时,按这个顺序选一定更优
- 临项扰动完得到的序列不一定就是答案,可能还得在上面做DP什么的
但现在并不能都选完,于是懵了
发现若有两个元素都被选了,它们被选顺序与在按刚才方法排过序中这两个元素的顺序相同
即最优解是这个序列的一个子序列
于是可以DP,令 \(dp[i][j]\) 为考虑到第 \(i\) 个物品,选了 \(j\) 个的最小时间
- 仔细检察一下有没有什么东西是倍增/指数级的
于是当 \(a_i>0\) 时,所需时间随选的个数(至少)倍增,于是只能选log个
所以第二维开到log,这部分复杂度 \(nlogn\)
然后考虑将 \(a_i=0\) 的贡献加到答案里
所以将这些 \(b_i\) 排序求前缀和,枚举所有合法状态并二分出最多再选多少个 \(a_i=0\) 的
特别注意一个细节是从一个商店到另一个需要1的时间
所以求前缀和时每个位置要额外加1
这部分我的实现是 \(O(nlog^2n)\) 的,跑的还挺快的
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
ll a[N], b[N], T;
namespace force{
int p[N], ans;
void solve() {
for (int i=1; i<=n; ++i) p[i]=i;
do {
int cnt=0; ll t=1;
for (int i=1; i<=n; ++i) {
if (t+a[p[i]]*t+b[p[i]] <= T) {
++cnt;
t+=a[p[i]]*t+b[p[i]]+1;
}
else break;
}
ans=max(ans, cnt);
} while (next_permutation(p+1, p+n+1));
printf("%d\n", ans);
exit(0);
}
}
namespace task{
int tot, ans;
ll dp[N][30], minn[30], sum[N], tem[N];
struct ele{ll a, b; inline void build(ll x, ll y) {a=x; b=y;}}p[N];
inline bool operator < (ele a, ele b) {return b.a*(a.b+1)<a.a*(b.b+1);}
void solve() {
// cout<<double(sizeof(dp)+sizeof(p))/1000/1000<<endl;
for (int i=1; i<=n; ++i) p[i].build(a[i], b[i]);
sort(p+1, p+n+1);
// cout<<"p: "; for (int i=1; i<=n; ++i) cout<<p[i].a<<','<<p[i].b<<' '; cout<<endl;
for (int i=n; i; --i)
if (!p[i].a) tem[++tot]=p[i].b;
else break;
sort(tem+1, tem+tot+1);
tem[0]=-1;
for (int i=1; i<=tot; ++i) sum[i]=sum[i-1]+tem[i]+1;
memset(dp, 0x3f, sizeof(dp));
dp[0][0]=1;
for (int i=1; i<30; ++i) minn[i]=T+1;
minn[0]=1;
for (int i=1; i<=n; ++i) if (p[i].a) {
for (int j=29; j; --j) if (minn[j-1]<T) {
dp[i][j]=(p[i].a+1)*minn[j-1]+p[i].b;
minn[j]=min(minn[j], dp[i][j]+1);
}
}
for (int i=0; i<=n; ++i) if (p[i].a) {
for (int j=29; ~j; --j) if (dp[i][j]<=T) {
ans=max(ans, int(j+(upper_bound(sum+1, sum+tot+1, T-dp[i][j])-sum-1)));
}
}
printf("%d\n", ans);
exit(0);
}
}
signed main()
{
freopen("eriri.in", "r", stdin);
freopen("eriri.out", "w", stdout);
n=read(); T=read();
for (int i=1; i<=n; ++i) a[i]=read(), b[i]=read();
// force::solve();
task::solve();
return 0;
}