ABC224做题笔记
Atcoder Begineer Contest 224
D - 8 Puzzle on Graph
题目大意
给定一个 \(9\) 个顶点,\(m\) 条边的图,共有八个棋子分别在 \(p_1,p_2,p_3...p_8\),问最终能否让第 \(i\) 个棋子放在 \(i\) 号节点上。
解题思路
考虑与八数码相同的做法。
将九个顶点对应的状态压缩成一个九位数,即每个定点上对应了哪个棋子。
令第 \(i\) 个定点上最开始的棋子编号是 \(r_i\),没有棋子的顶点 \(u\) 的 \(r_u\) 为 \(9\),就能够生成初始状态。
然后将这个九位数压入队列,跑 BFS 求解,再用一个 map 记录,最后判断能否达到 \(123456789\) 这个状态。
生成初始状态部分:
string str="999999999"; //一开始都没有摆棋子
for(int i=1,pos;i<=8;i++){
cin>>pos;
str[pos-1]=i+'0'; //读入第 i 个棋子的位置后替换相应位置
}
BFS 部分:
mp[str]=1;
q.push(str);
while(!q.empty()){
str=q.front();
q.pop();
int u=1;
for(int i=0;i<9;i++){
if(str[i]=='9'){
u+=i; //找到没有棋子的顶点的位置
break;
}
}
for(auto v:G[u]){
string n_str=str;
swap(n_str[u-1],n_str[v-1]);
if(mp[n_str]){
continue;
}
mp[n_str]=mp[str]+1;
q.push(n_str);
}
}
if(!mp["123456789"]) cout<<-1<<"\n"; //如果没有走过,就说明无法到达
else cout<<mp["123456789"]-1<<"\n";
E - Integers on Grid
解题思路
考虑设 \(rmax_i\) 表示第 \(i\) 列的最大值,\(cmax_i\) 表示第 \(i\) 行的最大值。
对数组进行排序,然后进行状态转移:
\(f_i=\max \{ rmax_{r_i},cmax_{c_i} \}\)
\(rmax_i=\max \{ rmax_{r_i},f_i+1 \}\)
\(cmax_i=\max \{ cmax_{c_i},f_i+1 \}\)
答案依次输出 \(f_i\) 即可。
cin>>h>>w>>n;
for(int i=1;i<=n;i++){
cin>>r[i]>>c[i]>>a[i];
mp[INF-a[i]].push_back(i);
}
for(auto p:mp){
vector<int> now=p.second;
for(auto v:now){
f[v]=max(f[v],max(rmax[r[v]],cmax[c[v]]));
}
for(auto v:now){
rmax[r[v]]=max(rmax[r[v]],f[v]+1);
cmax[c[v]]=max(cmax[c[v]],f[v]+1);
}
}
for(int i=1;i<=n;i++){
cout<<f[i]<<"\n";
}
F - Problem where +s Separate Digits
题目大意
有一个只含 \(1 \ldots 9\) 的长度为 \(N\) 数字串。
在这个串中加最少 \(0\) 个,最多 \(N-1\) 个加号,使得其变成一个加法算式,求所有可能的算式的结果的总和模 \(998244353\)。
解题思路
考虑 dp
-
设 \(f_i\) 表示 \(1 \ldots i\) 中,第 \(i\) 位与第 \(i-1\) 位之间没有加号的方案数
-
设 \(g_i\) 表示 \(1 \ldots i\) 中,第 \(i\) 位与第 \(i-1\) 位之间有加号的方案数
容易得到状态转移:
- \(f_i = s_i \times 2^{i-1} + f_{i-1} \times 10\)
解释:第 \(i\) 位与第 \(i-1\) 位之间没有加号,即它们是连着的一个数,因此需要先将 \(f_{i-1}\times 10\),再将第 \(i\) 位的数字加上去,共有 \(2^{i-1}\) 种可能,因此就要加上 \(s_i \times 2^{i-1}\)。
- \(g_i = g_{i-1} \times 2 + f_{i-1}\)
解释:第 \(i\) 位将第 \(i-1\) 位没有分离的分离开来了,因此要加上 \(f_{i-1}\),同时之前就已经分离的会出现两个分支因此还要加上 \(g_{i-1} \times 2\)
最终答案为 \(f_n + g_n\),时间复杂度 \(O(N)\),\(1 \leq N \leq 2 \times 10^5\),轻松通过。
n=s.length();
pow_2[0]=1ll;
for(int i=1;i<=n;i++){
pow_2[i]=(pow_2[i-1]*2ll)%mod;
}
for(int i=1;i<=n;i++){
ll u=s[i-1]-'0';
f[i]=((f[i-1]*10)%mod+(u*pow_2[i-1])%mod)%mod;
g[i]=((g[i-1]*2)%mod+f[i-1])%mod;
}
cout<<(f[n]+g[n])%mod<<"\n";
G - Roll or Increment
题目大意
有一个 \(N\) 面骰子,最初,骰子 \(S\) 面朝上。
可以进行以下两次操作,次数不限。
-
操作一:花费 \(A\) 代价,使骰子显示的数值 "增加" \(1\)。
-
操作二:花费 \(B\) 代价,重新投掷骰子。
要让骰子从初始状态显示 \(S\) 变为显示 \(T\)。
求当使用最优策略使该期望值最小时,这样做所需花费的最小期望值。
解题思路
显然,应该操作一还是操作二,只取决于骰子当前显示的数字。
对于每一个 \(i = 1, 2, \ldots, N\),试求 "当骰子显示 \(i\) 时,我们应该选择哪个选项"。
注意到:
-
如果骰子显示的数字大于 \(T\),那么显然操作二是最佳选择。
-
在操作一之后立即操作二显然比仅仅操作二更劣。
因此,在最优策略中,如果当骰子显示的数字为 \(P\) 时选择操作一,那么当骰子显示的数字为 \(P'\) 使得 \(P \leq P'\) 时,应该选择操作一。
根据上述两点,开始思考最优策略:
存在一个满足 \(1 \leq X \leq T\) 的整数 \(X\):
如果骰子显示 \(T-X\) 和 \(T\) 之间的任意一个数,则选择 操作一;
否则,选择 操作二。
问题转化为:
求选择一个整数 \(X\) ,使得 \(1 \leq X \leq T\) ,从而使得 "将骰子所示的骰子从 \(S\) 改为 \(T\) 的期望值"最小。
开始分类讨论:
- 在" \(T-X+1\leq S \leq T\) 成立 "的条件下选择 \(X\)。
- 在" \(T-X+1\leq S \leq T\) 不成立 "的条件下选择 \(X\)。
第一种情况:在 "成立 \(T-X+1\leq S \leq T\) " 的条件下选择 \(X\) 时。
因为我们选择 操作一,直到骰子显示的数字从 \(S\) 变为 \(T\) ,所以所需的代价是 \(A(T-S)\) ,与 \(X\) 无关。
第二种情况:在" \(T-X+1\leq S \leq T\) 不成立 "的条件下选择 \(X\) 时。
骰子上的数字从 \(S\) 到 \(T\) 的转换过程包括以下两个步骤。
- 首先,重复操作二,直到骰子显示的数字在 \(T-X+1\) 和 \(T\) 之间。
- 然后,重复"增加骰子显示的数字",直到数字变成 \(T\)。
每一步所需的花费如下。
-
进行一次操作二后,骰子显示 \(T-X+1\) 和 \(T\) 之间数字的概率 "为 \(X/N\),因此第一步中操作二的预期次数为 \(N/X\)。第一步的花费为 \(BN/X\)。
-
第一步结束后,模具有可能出现介于 \(T-X+1\) 和 \(T\) 之间的任何整数。因此,第二步的花费为 \(\lbrace\sum_{i = T-X+1}^{T} A(T-i)\rbrace/X = A(X-1)/2\)。
将骰子显示的数字从 \(S\) 变为 \(T\) 所需的花费为 \(A(X-1)/2 + BN/X\)。
那么如何在 \(T-X+1\leq S \leq T\) 不成立 "的条件下,求出选择整数 \(X\) 时 \(T-X+1\leq S \leq T\) 的最小值呢?
考虑函数 \(f(x) = A(x-1)/2 + BN/x\) 定义为正实数 \(x\)。
可以观察图像计算可得,函数 \(f(x)\) 在 \(x = \sqrt{2BN/A}\) 处最小。
另外,由于 \(f''(x) = 2BN/x^3\) , \(f(x)\) 是一个凸函数。
因此,只需计算 \(f(X)\) 在 \(\sqrt{2BN/A}\) 附近的若干个 \(X\) ,使得 "不成立 \(T-X+1\leq S \leq T\) "和 \(1 \leq X \leq T\)。
时间复杂度 \(O(1)\)。

浙公网安备 33010602011771号