2 条题解
-
0
思路:
大神题。
考虑递归,我们令 表示此时已知下标 的所有值时最多用 的价钱可以知道下标 的值;初始使用 。
对于一个 来说,首先调用 ,得到一个购买子序列 (设 ),此时显然可以得到这个子序列的价值之和 ,考虑这个子序列的平均数 ,显然此时不可能买到 (这里 是为了防止 时再次买到)。
然后考虑子问题用 去更新现在的 ,此时考虑 后面所有大于等于 的位置,用 减去其的贡献,重新找平均数 的价格进行调用递归,不断逼近,直到 。
显然最终会先递归到 等于 的情况(因为当 且 不等于 时会使用 的价格去买,继续往下);同理,这会始终保证 ,因为对于一个 ,其递归的下一个 一定比它小,所以对于一个 来说,只有 的 可能会买到它,且每个 只会作为一次 。
最后你已经求出了 ,补全 次 即可。
完整代码:
#include <utility> #include <vector> #include <bits/stdc++.h> using namespace std; typedef long long ll; #define fi first #define se second std::pair<std::vector<int>, long long> transaction(long long M); const int N = 105; ll val[N]; int cnt[N]; inline int dfs(ll w, int now){ auto t = transaction(w); auto A = t.fi; ll r = t.se; w -= r; for(auto v : A) ++cnt[v]; while(A.back() >= now){ w -= val[A.back()]; A.pop_back(); } while(A.front() + 1 < now){ now = dfs((w - 1) / A.size(), now); while(A.back() >= now){ w -= val[A.back()]; A.pop_back(); } } val[A.front()] = w; return A.front(); } void buy_souvenirs(int N, long long P0){ dfs(P0 - 1, N); for(int i = 1; i < N; ++i){ while(cnt[i] < i){ ++cnt[i]; transaction(val[i]); } } }注意:这是在某交互大神帮助下完成的该题,并非独立做出。
-
0
秒赤,好玩。
很神秘的题意,交互但是目标不是得到某个值而是完成一个目标,第一次见。
我们先把交互库返回的东西处理一下,我们想要返回的几样商品的价值和,而不是剩下的钱。我们记返回值为
[a,b,c,...],x,方括号里的是商品集合, 是它们的价值和。首先我们发现询问 的值是不合法的,不然 就买了;过于小的值也是很危险的,毕竟你不知道 是什么,万一小于它你就买不到东西,也是不合法的。那么第一步我们能干什么?唯一安全的值应该是 。
知道了这个后,我们来手玩一下小数据,比如 。
第一步问 ,会有两种情况:
- 返回
[1] x。这下我们直接知道 ,接着问一次 就能知道 了。 - 返回
[1,2] x。这该怎么办!1 号商品已经没有剩余购买次数了,下一步只能买一个 2。这意味着我必须找出一个 来询问。容易发现 满足条件。
这下 就解决了,拼盘 39pts 到手。
这个过程中,有几个点很有启发性:
-
找出一个 来询问:如果我们能够对每个 ,都找到一个 询问一次,这样获得的信息会构成一组 元线性方程组,而且是满秩的(因为这样一次询问, 商品一定会被买,且只有 的商品可能会被买,因此是对角线全为 1 的上三角),可以直接消元得到每一个 。而且如果对于每个 恰好询问一次,一定能满足 被买了 次,不够的在解出 后补上即可。
-
满足条件:这提示我们使用上次询问的商品的平均价格来进行下次询问。因为这个平均一定会落在最大值和最小值之间,至少是安全的。
-
知道 的值后, 一定属于 ,可以直接用。
有了这些前提后,我们定义一个求解函数 ,其中 是一个商品编号集, 是 中商品的价值和(这个信息是由一次向交互库询问得来的)。这个函数做的事情是,求出 的所有商品的具体价值。
记 ,那么显然这个 是上一次询问了一个 得来的。为了满足每个 恰好询问一次,我们需要保证 只会作为 出现一次。这个记忆化一下就好。
具体做法是:
- 每次进入这个函数,所有已知商品价格的 一定形成一段后缀(这个是函数本身的进行过程保证的)。我们不断检查 的价格是否已经确定,如果是,就删除 并相应的更新 。(这一步其实是在消元)
- (如果此时 )基于启发点 (2),求出剩余物品的平均价格 并进行一次询问。注意此时 和 之间的所有值我们应该都不知道,相应的所有 也都没有被询问过,所以进行这个询问是合理的。询问完以后递归处理。
- 重复 1,2 两步直到 。此时容易发现 应该仍然是 ,这个时候显然 。由启发点 (3),此时我们获得了 ,如果 没有被处理过,那么询问一次 并递归处理。
最开始询问一次 并调用即可。
最后显然可能有物品没买够的,既然我们已经求出所有物品的价格,询问 就能恰好买一个 ,补上即可。
最后补一句,容易发现这个 5000 次交互是假的。每次至少得买一个,总共最多买 次,根本不怕这个次数上限。
- 返回
- 1
信息
- ID
- 3366
- 时间
- 1000ms
- 内存
- 2048MiB
- 难度
- 10
- 标签
- 递交数
- 11
- 已通过
- 1
- 上传者