[NOI 2026] 布丁
建议删除此题,我毫无逻辑的搞都过了,明显不是题,纯看谁运气好。
题目链接: [NOI 2026] 布丁 。
题目描述:
有多次询问,每次询问交互库会生成一个数 w ,你需要实现函数 int find_tastiness(int c,int m) 猜测并返回这个 w 。
你可以调用函数 int query_tastiness(vector<int>S) ,该函数会将 w 加入 S 后排序并输出相邻两项的 gcd 之和。
对于单组测试,你最多调用不超过 4 次该函数,传入的 S 最多不超过 35 。如果你超过了限制,你会获得相应的部分分。
w∈[1,m],m=3000 。
时间限制 12s 。
交互库不是自适应的。
很明显 gcd 的和不具备任何实际意义。你只能通过 query_tastiness 的结果和原来的 S 的相邻两项 gcd 做差才能得到有效信息。不妨设 query(S) 的结果为这个信息。
容易发现如果我传入序列 S={1,2,3,⋯,m} ,那么 query(S) 的结果就是 w 。这样做可以获得 16 分。
考虑调用 4 次 query ,前三次缩小 S 的范围,最后一次直接传入 S 。
一个简单的想法是随机 K 个长度为 n 的序列,手动计算对于每一个 w=i , query 的值。找到这 K 个序列中最能区分的序列,然后询问。复杂度 O(Knm) 。由于常数原因,取 n=9,K=10 后获得 50 分。
考虑优化。很明显如果 K 能更大,分数肯定更高。所以我们考虑优化常数。容易发现对于相同的 S ,我们其实重复计算了几乎 m 遍,所以考虑记忆化序列。我这里采用自然溢出哈希和 unordered_map 存储。在 n=9,K=2000 时获得了 86 分。
然后就是一些调参工作了。目前这样最高有 97 分,参数 n=6,K=4100 。
然后就是一些神人优化了。参考 NOI 2024 百万富翁,这题本质上是提交答案题,于是考虑打表。
具体来说,虽然我直接设置 K=105 会超时,但是我可以本地多跑几趟 K=4700 ,然后根据表找到更优秀的初始序列就行。这种方法最终使得调用 query 的 S 的和为 35 , 通过了 100 分。
其实这种方法的上限远不止于此:
- 可以本地多跑几趟,获得更优秀的初始序列。
- 可以换成更优秀的随即方式。
吐槽第一篇题解:
写过后想尝试一下 2 的优化方法。于是我去看第一篇题解。然后我发现第一篇题解中的方法:
A. 如果不打表只能靠运气,纯废物。
B. 没试。
C. 用完更劣质了。
D. 用完更劣质了。
E. 不知所云,不用试也知道会更劣质。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| #include <bits/stdc++.h> using namespace std;
#define WZH #ifdef WZH #ifndef PUDDING_H void init(int c, int t); int find_tastiness(int c, int m); int query_tastiness(std::vector<int> a); #endif #endif int G[3001][3001]; void init(int c,int t){ for(int i=1;i<=3000;i++)for(int j=1;j<=3000;j++) G[i][j]=__gcd(i,j); } using V=vector<int>; int query(V x){return query_tastiness(x);} mt19937 rnd((unsigned)time(NULL)^114514); int get_value(V &a,V &book){ int n=a.size(),mx=0; V tmp(6001,0); int t=0; for(int i=1;i<=3000;i++){ if(book[i])continue; int ans=0; while(t<n&&a[t]<=i)t++; if(t)ans+=G[a[t-1]][i]; if(t<n)ans+=G[a[t]][i]; if(t&&t<n)ans-=G[a[t]][a[t-1]]; tmp[ans+3000]++; mx=max(mx,tmp[ans+3000]); } return mx; } void upd(V &a,V &book){ int n=a.size(),k=query(a),t=0; for(int i=0;i<n-1;i++)k-=G[a[i]][a[i+1]]; for(int i=1;i<=3000;i++){ if(book[i])continue; int ans=0; while(t<n&&a[t]<=i)t++; if(t)ans+=G[a[t-1]][i]; if(t<n)ans+=G[a[t]][i]; if(t&&t<n)ans-=G[a[t]][a[t-1]]; if(ans!=k)book[i]=1; } } V Max(V &a,V &b,V &book){ int t1=get_value(a,book); int t2=get_value(b,book); return t1<t2?a:b; } using ull=unsigned long long; const int P=133; unordered_map<ull,vector<int>>mp; ull HASH(V a){ int n=a.size(); ull h=0; for(int i=0;i<n;i++)h=h*P+a[i]; return h; } const ull bh=HASH(V(3001,0)); int find_tastiness(int c,int m){ if(c==1){ V t; for(int i=1;i<=m;i++)t.push_back(i); return query(t)-34; } V book(3001,0); for(int i=0;i<3;i++){ int n=6; V ans(n,0); ull T=HASH(book); if(!mp.count(T)){ if(T==bh)ans={138,570,1380,1475,1645,2310}; else ans={3,1908,2526,2636,2704,2794}; for(int j=1;j<=4400;j++){ V t(n,0); for(int k=0;k<n;k++)t[k]=rnd()%3000+1; sort(t.begin(),t.end()); ans=Max(ans,t,book); } mp[T]=ans; } else ans=mp[T]; upd(ans,book); } V t; for(int i=1;i<=3000;i++)if(!book[i])t.push_back(i); int T=0,M=t.size(); for(int i=0;i<M-1;i++)T+=G[t[i]][t[i+1]]; return query(t)-T; }
|