Spark _Exam_ 20240715
Spark Exam 20240715
Conclusion
SB 出题人出 DP 场,T1靠小常数通过不给提示干死选手,T2出题人认为思维难度低代码5KB,NOIP场的T3放黑题,T4又是区间DP \(\mathcal O(n^6)=117649000000\) 竟然能够通过?你代码常数真的小!
好的喷完了。这种场的后果就是,平均分 50,最高 90,最低 0
实际上如果真的好好打暴力的话能够上 100……
T1真的可惜,复杂度算错了就没打 -50pts
T4状压加区间DP能够拿 40pts
T3暴力能够拿到20pts
ideal160吧,这个还是在能力范围内的(那就榜二了
A. 魔力屏障 (magic)
Statement
有一排玻璃每个一个坚固程度 \(a_i\) ,现可以在任何位置向右边滚出一个威力为 \(x\) 的球,若球碰到玻璃 \(i\) ,且 \(a_i\le x\) ,则玻璃碎掉并 \(x\to \lfloor x/2\rfloor\) ,否则球在此处停下,若球相遇,可合成一个大球并威力相加并继续滚,求对于每个 \(i\) ,击碎玻璃 \([1,i]\) 需要的最小威力总和。
\(1\le n\le 70,1\le a_i\le 150\)
Solution
考虑并不是从左边往右边击碎就是最优,例如 \(100,1,50\) ,击碎 \(100\) 消耗的代价会在 \(1\) 那个地方浪费很多,不如先打碎 \(1\) 。
所以这个不是一个线性的过程,而是不按顺序的,又发现一个球影响的是一个区间,考虑区间 DP。注意到需要多设一个击碎区间之后剩余威力才能转移,即 \(f_{l,r,k}\) 表示击碎 \([l,r]\) 且剩余 \(k\) 的最小代价。那么考虑怎么设计转移,我们是为了这个顺序才采用区间 DP,不妨考虑将 \([l,r]\) 分成若干区间并选择一个顺序来打碎,这样我们就拥有了一个很劣的转移,考虑这样转移一次是 \(\mathcal O(n!2^n)\) 。哦真是太恐怖了。
但是这有什么关系呢?难道这样做真的是必要的吗?区间的划分不是在这个区间的子区间就考虑过了吗,为何再考虑呢?那么,我们就可以优化:
枚举分界点 \(k\) ,设 \(k\) 就是最后打碎的玻璃,那么,左边区间的最优序列和右边的最优序列拼起来就是大区间的最优打碎顺序,他们剩余的威力加起来就是大区间剩余的魔力
这样就成功优化为 \(\mathcal O(n^3V^2)\) ,其中 \(V=\sum a_i\) ,答案是不可能超过这个的(每个单独打碎)
但是考虑到,没有必要保存太多威力,威力如果不是为了被使用到靠近的玻璃,那么肯定不是最优的,因为中间会浪费威力,不如要用的时候再在靠近的位置添加。所以 \(V=\max a_i\) 是可以的。
这样其实还是不可以过。但是要注意到,这个 DP 是带 \(\frac{1}{8}\) 小常数的,这是因为:区间DP不会枚举 \([l,r],r<l\) 这样的区间,这贡献 \(\frac{1}{2}\) ;留存的威力不可能比最后一个玻璃的坚固程度的一半还小,共两次枚举,一共贡献 \(\frac{1}{4}\) 。
好吧这样还是不可以过(964,687,500),你说的对,但是《O2优化》是由 ISO C++ 委员会 自主研发的一款全新开放世界冒险游戏。游戏发生在一个被称作「OI」的幻想世界,在这里,被 CCF 选中的人将被授予「C++」,导引算法之力。你将扮演一位名为「OIer」的神秘角色,在自由的旅行中邂逅性格各异、能力独特的同伴们,和他们一起击败强题,找回失散的1=——同时,逐步发掘「NOI」的真相。
哈哈哈赛时想到了结果你告诉我小常数能过哈哈哈结果没写哈哈哈,(发癫)(旋转)(阴暗爬行)(变成猴子)(抢走游客的钱包)(上蹿下跳)(一巴掌拍扁tfqz)(吗的玩不了了)(变成大粉兔)(一拳把地球打爆)(鉴定为学竞赛学疯了)
B. 诡秘之主 (mystery)
Statement
现有若干棵二叉搜索树和一个未确定的非负整数序列 \(a_i\) ,给定一个 01 串 \(s\) ,\(s_i\) 表示 \(a_i\) 是不是 \(0\) ,回答如下询问:若分别依次插入 \([l,r]\) 的所有子区间中的数,每个区间形成的二叉搜索树最小的可能深度之和是多少。
Solution
C. 博弈 (game)
Statement
有一个树形的迷宫,里面有一只老鼠,现在你要把老鼠从 \(S\) 逼到 \(T\) ,你每轮都可以做以下操作之一:1. 删除边 2. 解锁边 3. 不操作(不计操作数)。老鼠每轮可以选择一条没有封锁没有删除的出边并前往,如果没有出边则不动,但是有出边的时候不能不走,老鼠走过一条边,就会封锁这条边。
你先手。双方轮流操作,你要最小化操作次数,老鼠要最大化你的操作次数,若你们都按照最优策略行动,请问这个操作次数最后是多少?
\(1\le n\le 10^6\)
这是一道 CEOI 的原题:P4654 [CEOI2017] Mousetrap - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
NOIP T3 出黑题的出题人是屑 😦 但是这题还是很好玩
Solution
遇到博弈论,我们必须优先搞清楚各方在不同情况下应该有什么策略,我们应该从不同的情况开始模拟。我们看到,Subtask给了一个奇怪的特殊性质: 对于 \(25\%\) 的数据,存在边 \((S,T)\) 。
这时不难想到让 \(T\) 作为根看看。那么最开始的时候,我们的老鼠同学肯定会往子树中的一个逃窜,由于它走过之后边会被封锁,所以它回不去,最后肯定被困在叶子上。这个时候老鼠不能动,则操作者就必须把老鼠一路上走过的所有分叉和出发点的其他儿子全部删除,然后再解锁所有被封锁的边,这样老鼠就只能往上到 \(T\) 。
那么,要让操作者的操作次数尽量多,老鼠就会选择进入代价第二大的子树,因为最大的子树一开始就会被操作者删除。
那么就可以树形 DP 求出这个 \(f_x\) 的值。
现在考虑一般情况。此时老鼠不一定一开始就钻 \(S\) 的子树,也许先向上走到一个较大子树再钻进去。而操作者次数会利用这段时间删掉一些 \(f\) 较大的边防止老鼠钻进去。
不过,因为两边都会看对方怎么操作,直接计算不好算,可以发现答案具有单调性,可以二分答案进行判定。
那么,假设我们是操作者,模拟一次老鼠的操作,我们假设老鼠钻进了一个子树 \(z\) ,那么我们就会花费 \(f_z+g_z\) ,其中 \(g_z\) 表示从 \(z\) 到根的分叉数量。如果这个大于了我们剩余的步数,那么必须花费一步来堵住它。如果当前花费的步数已经超过答案,或者我们来不及阻止老鼠走进这个分支(当前要必须操作的次数大于操作轮数),那这个答案就不可行。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
vector<int> e[N];
int n,S,T,f[N],g[N],deg[N],fa[N];
void dp(int x,int pa){
fa[x]=pa;int son=deg[x]-(bool)pa;
if(pa) g[x]+=son-1;
int mx=0,se=0;
for(int v:e[x]){
if(v==pa) continue;
g[v]=g[x];
dp(v,x);
if(f[v]>=mx) se=mx,mx=f[v];
else if(f[v]>=se) se=f[v];
}
f[x]=se+son;
}
bool check(int x){
int cur=S,lst=-1,cnt=0,rnd=0;
while(cur!=T){
int now=cnt;
for(int v:e[cur]){
if(v!=lst and v!=fa[cur] and
f[v]+g[cur]-(lst!=-1)+1>x-now) cnt++;
}
if(cnt>rnd+1 or cnt>x) return false;
lst=cur,cur=fa[cur],rnd++;
}
return true;
}
int main(){
cin>>n>>T>>S;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
e[u].push_back(v),e[v].push_back(u);
deg[u]++,deg[v]++;
}
dp(T,0);
int l=0,mid,r=1e9,ans=0;
while(l<=r){
mid=(l+r)/2;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans;
}
D. 地雷 (landmine)
Statement
有一个长度为 \(n\) 的四元组序列 \((p_i,q_i,r_i,s_i)\) ,现删除一个元素 \(i\) 的代价是 \((p_{i-1}-q_i)^2+(p_i-r_{i+1})^2+(p_{i+1}-s_{i+2})^2\) ,删除之后两边的元素会合拢。求最大代价。
\(1\le n\le 70\)
Solution
我们发现本问题是多次决策型的,一次决策对后续决策代价的影响总是相邻的……
好吧,我宣布,本题最大的观察是,看出这题是区间 DP。
怎么说呢,就是你感觉一下,对于一个 \(x\) 来说,选择它时决定了它代价的那几个数可能和他隔着一个区间。就考虑这个 \(x\) 究竟有什么性质,欸,那么这个 \(x\) 就是 它左边那个元素和右边第一个元素区间里面最晚被删的数。那么对于一个区间,它里面的最晚删的数就会被端点影响。(强词夺理真是困难)

如果看出上面一点,那么转移的时候枚举区间最晚删除的数就是自然的操作了,但是状态设计甚至还没有,那么,设 \(f_{l,r,t}\) 就是区间 \([l,r]\) 中的数全部被删除,且 \(t\) 是在删除区间中最晚删除的数的时候,还存在于 \(r+1\) 右边的第一个数。换言之,这个就枚举了右边第二个数到底是什么,为什么要这样呢,因为要成为右边第二个数,是有条件的,这要求,这个数 \(t\) ,中间的数,也就是 \((r+1,t)\) 必须先被删除了。
这样难道就可以了吗?读者如果试一下,就会发现这个限制光有这个也还是不能保证满足,还必须设 \(f_{l,r,t,u}\) 中的 \(u\) 表示 \([l,u)\) 中的数都比 \(u\) 先删除,这下就可以转移了。
我们此时枚举 \(k\) 代表区间中最后一个删除的数,\(v\) 为左边区间方程中的 \(t\) 。
写出方程:
您有没有注意到什么不对,第一项 \(f_{l,k-1,v,u}\) 的 \(u\) 一直是相同的,那么岂不是只有 \(l=u\) 的状态才合法??
您有没有注意到上面 \(v\) 的范围是开的?而它实际上取得到?
考虑 \(v=k\) ,此时代表“ \(v\) 左边必先删除,然而 \(k=v\) 是最后一个选的数”,所有实际上这个限制重复了,当 \(v=k\) 的时候,不应该有任何限制,所以应该允许此时从 \(u=l\) 的位置转移。对于 \(v=r+1\) 的情况,同理。
很好我们得到了 \(\mathcal O(n^6)\) 小常数算法,吸氧气可过。
Let's! Get! Higher!
本文来自博客园,作者:haozexu,转载请注明原文链接:https://www.cnblogs.com/haozexu/p/18304180

浙公网安备 33010602011771号