题解:P13535 [IOI 2025] 纪念品(souvenirs)
IvanZhang2009 · · 题解
容易发现我们需要让每个物品(除了第
考虑我们一开始只能问 solve(i) 表示我们得到了
如果一开始得到的是多个物品的价值之和呢?可以发现我们能猜的只有平均价,因为要保证猜的价格不超过最大值,又要高于最小值。我们每次不妨每次找到所有未解决的询问里的最靠右的一个,如果它只有一个数,那就把这个后缀删掉,更新其他询问的信息;否则你就猜平均数,这样可以保证新加入的这个询问在当前的最后一个询问和最后一个没删的数之间,显然是不会和之前重复的。
重复这个过程一定可以结束。最后已经得到了所有物品的价格,把剩下的次数补上即可。
#include "souvenirs.h"
#include<bits/stdc++.h>
using namespace std;
#define MOD 998244353
#define ll long long
#define pii pair<int,int>
#define all(v) v.begin(),v.end()
#define pb push_back
#define REP(i,b,e) for(int i=(b);i<(int)(e);++i)
#define over(x) {cout<<(x)<<endl;return;}
#define lowbit(x) ((x)&(-(x)))
#define cntbit(x) __builtin_popcount(x)
#define deal(v) sort(all(v));v.erase(unique(v.begin(),v.end()),v.end())
#define lbound(v,x) lower_bound(all(v),x)-v.begin()
int n;
int cnt[105];
ll val[105];
deque<int>dq[105];
ll cost[105];
ll ask(ll x,int y){
//(2z+y-1)*y/2<=x
return (x*2/y+1-y)/2;
}
void clear_(){
REP(i,0,n)if(dq[i].size()&&dq[i].back()==n-1)dq[i].pop_back(),cost[i]-=val[n-1];
--n;
}
void solve(int x){
if(x==n-1)return;
auto [t,c]=transaction(val[x]-1);
++x;
for(auto i:t)++cnt[i];
cost[x]=val[x-1]-1-c;
for(auto i:t)if(val[i]==-1)dq[x].pb(i);
else cost[x]-=val[i];
while(x<n){
int lst=x;
REP(i,x,n)if(dq[i].size()&&val[lst]==-1)lst=i;
if(dq[lst].size()==1){
val[lst]=cost[lst];
solve(lst);
clear_();
continue;
}
ll p=ask(cost[lst],dq[lst].size());
auto [t,c]=transaction(p);
int y=t[0];
cost[y]=p-c;
for(auto i:t)++cnt[i];
for(auto i:t)if(val[i]==-1)dq[y].pb(i);
else cost[y]-=val[i];
}
}
void buy_souvenirs(int N, ll lst) {
n=N;
REP(i,0,n)cnt[i]=0,cost[i]=-1,val[i]=-1,dq[i].clear();
val[0]=lst;
solve(0);
REP(i,0,N){
while(cnt[i]<i)transaction(val[i]),++cnt[i];
}
}