0%

题目大意

两个字符串uuvv之间的编辑距离是将uu转换为vv的最小编辑操作次数。可以对字符串应用三种编辑操作:插入字符,删除字符,用不同字符替换某个字符。

例如,我们可以用四次替换将hello转换为world,因此编辑距离最多为44。您可以用两次替换和一次插入将wally转换为walter,因此编辑距离最多为33。计算两个字符串之间的编辑距离是一个众所周知的问题,有许多应用。

当前任务是计算一个字符串到最接近的回文串的编辑距离。回文串是从后往前读和从前往后读相同的字符串,例如madam。

hello到最接近回文串的编辑距离仅为22:我们可以用两次编辑操作将hello转换为ollo,或hllh,或elle。

编写一个程序,可以找到一个单词到最接近回文串的距离。

思路讲解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
auto dfs=[&](auto && self,ll l,ll r) -> ll {
if (r==l) {
return 0;
}
if (r-l==1) {
return s[l]!=s[r];
}
if (dp[l][r]!=INF) {
return dp[l][r];
}
dp[l][r]=min({self(self,l+1,r-1)+(s[l]!=s[r]), // 替换
self(self,l,r-1)+1, // 删除右侧(等价于左侧插入一个相同的抵消)
self(self,l+1,r)+1, // 删除左侧(等价于在右侧插入一个相同的)
});
return dp[l][r];
};

AC代码

AC
https://codeforces.com/gym/106084/submission/361098919

心路历程(WA,TLE,MLE……)

题目大意

Jerry和Tom正在一个有nn个顶点的有向图GG上玩一个游戏,顶点编号从11nn。对于每个顶点1u<n1 \le u < n,从uuu+1u+1有一条有向边。此外,还有mm条额外的有向边。第ii条额外边从uiu_iviv_i

GG具有以下特殊性质:不存在两条有向边(uivi)(u_i\to v_i)(ujvj)(u_j\to v_j)使得ui<uj<vi<vju_i < u_j < v_i < v_j

在游戏开始时,Jerry和Tom分别站在顶点xxyy上,其中xyx \ne y。游戏按照轮次进行。在每个轮次中,玩家根据以下规则行动,Jerry先行,然后是Tom:

  • Jerry 必须选择从他当前顶点出发的一条出边****并沿着它移动到终点。如果他当前顶点没有出边,则他保持原地

  • Tom 可以选择从他当前顶点出发的一条出边并沿着它移动到终点,或者选择不移动并保持原地。

如果在任何轮次结束时,Jerry和Tom在同一个顶点上(包括在顶点nn),游戏立即结束且Tom获胜。否则,如果Jerry最初在顶点nn,或在任何轮次结束时到达顶点nn,Jerry获胜。

注意,如果在一轮之后,Jerry和Tom都在顶点nn,则Tom获胜

在整个游戏过程中,两个玩家都知道对方的位置。

可以证明游戏会在有限轮次内结束。

对于一对整数1x,yn1 \le x,y \le nxyx \ne y,定义f(x,y)f(x,y)如下:

  • Jerry和Tom将进行一个游戏,其中Jerry从顶点xx开始,Tom从顶点yy开始。Tom想赢,但他也想最小化他实际移动的次数(即,他改变顶点的轮次数;原地不算移动)。假设双方都以最佳方式行动,如果Tom不能赢则令f(x,y)=0f(x,y)=0;否则,令f(x,y)f(x,y)为Tom强迫赢所需的最小移动次数。如果Tom在最佳游戏中获胜,Jerry仍会尽力最大化Tom必须移动的次数。

计算

1x,yn,xyf(x,y). \sum\limits_{1 \le x,y \le n, x \ne y}f(x,y).

思路讲解

我们梳理一下题意,并进行一下样例解释

image

题目不允许这样子的两条边存在。

image

在样例 2 中,贡献为 1 的两种情况。

如果说没有额外的边,那么答案就是 00。因为 Tom 在 Jerry 前面,反正肯定赢不了。Tom 在 Jerry 后面,守株待兔即可。可以发现,无论什么情况,根据定义,都是 00

我们现在这个问题还是回来,什么时候肯定不会有这个追及,有这个贡献?其实就是如果把额外边当作区间加法的话,Tom 在没有被任何区间覆盖的地方,肯定是不会有答案贡献的。

不过我们发现,这样子一种一种情况看,实在是太慢了。既然是博弈题目,那么Tom 和 Jerry 的最优策略是什么呢?就是每步都走最远的边

因此,我们不妨把 Tom 走的路径,以及 Jerry 走的路径绘画在一张图上(按照上面说的最优策略),这张图,其实是一颗树。

或者,换句话说,我们只保留每个点的最长出边(这样子就可以形成一颗树),较短的出边,我们直接删除(因为不符合)。

接着就是来处理这个贡献与计算问题了。

我们想要计算的就是,当我们已经知道 一个点 uu,在其他所有树上,比他的深度浅或者同样深度的所有点,记作 VV 集合,贡献为:

vVdep(u)dep(lca(u,v))\sum_{v\in V}\text{dep}(u)-\text{dep}(\text{lca}(u,v))

这个问题,有许多解决方法,不过,我们这里选择使用启发式合并。

启发式合并的关键就是,合并过程复杂度必须要依赖于轻儿子的这个子树的参数(如节点数量,深度等),不能够依赖于遍历重儿子。

不过,这个要求并不难,使用前缀和,搞一搞,就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 计算答案,把轻儿子节点合并进主集合中。
for (auto [dep_son,cnt]:node_dep_cnt_mtx[to]) {
// cnt 是深度为 dep_son,较大的节点的数量(Jerry),get_presum_val 拿到的
// 是所有比 dep_son 小的点的 dep(v)-dep(lca(u,v)) 的这个和
ans+=cnt*get_presum_val(dep_son);
// 深度相同的节点可以反复贡献(可以理解为上面也可以贡献,下面也可以贡献)
ans+=cnt*node_dep_cnt_mtx[u][dep_son]*(dep_son-dep);
// 这个是深度比 dep_son 大的节点的数量
ll num_gt_dep=sum_node-get_presum_num(dep_son);
// 深度比 dep 深度深的节点,也会贡献,因为已经在重儿子中的深度深的节点
// 合并进来的时候还没有这样一个节点,贡献的就是 dep_son-dep(lca)
ans+=num_gt_dep*cnt*(dep_son-dep);
// 这里是顺手进行一个更新
node_dep_cnt_mtx[u][dep_son]+=cnt;
}

注意这个有可能出现重儿子比轻儿子的这个链长短的这个情况(当然可以变为分长儿子/短儿子)。这个时候直接取这个前缀和,因为我们用 map 实现,就会取到 00。这个时候可以用一个匿名 get 函数特判一下这个情况。

这种情况的一个 hack 数据:

1
2
3
4
5
6
1
7 2
3 7
4 6

ans:20

AC代码

AC
https://codeforces.com/contest/2188/submission/361002651

心路历程(WA,TLE,MLE……)

题目大意

给定长度为nn的数组aa。对于整数kk,我们称其为piggy,当且仅当通过执行以下操作任意次数(可能为零),可以将aa以非递减顺序排序:

  • 首先,选择两个索引iijj1i<jn1 \le i < j \le n),使得aiajk|a_i - a_j| \ge k;

  • 然后,交换aia_iaja_j

您需要确定最大的piggy整数kk。如果不存在这样的整数,则输出1-1

思路讲解

不难注意到,我们可以选用最大的数字或者最小的数字去做中转站,

image

乍一看,好像选择最小的数字,还是选择最大的数字,取决于两个数字 totonownow?实际上不是的,我们完全可以换着来,比如说:

image

这样子就实现了 totomnmnnownow 用这个 mxmx,反之亦然,完全可以只根据一个数去选择

okay,那就行了。我们发现,这个 totonownow 一定都是和有序序列中的同位置值不一样的点,因此,我们的答案其实就是:(其中 SS 为所有和有序序列中的同位置值不一样的点的下标集合)

ansminiS(max(mxai,aimn))ans\gets\min_{i\in S}(\max(mx-a_i,a_i-mn))

当然,也可以使用代码中的这个贪心实现。

AC代码

AC
https://codeforces.com/contest/2188/submission/360594620

心路历程(WA,TLE,MLE……)

题目大意

给定两个非负整数 xxyy。找到两个非负整数ppqq,使得p  &  q=0p\;\&\;q=0,且xp+yq|x-p|+|y-q|最小化。这里,&\&表示按位与操作

You are given two non-negative integers xx, yy. Find two non-negative integers pp and qq such that p  &  q=0p\;\&\;q=0, and xp+yq|x-p|+|y-q| is minimized. Here, &\& denotes the bitwise AND operation.

Input

Each test contains multiple test cases. The first line contains the number of test cases tt (1t1041 \le t \le 10^4). The description of the test cases follows.

The only line of each test case contains two non-negative integers xx and yy (0x,y<2300 \le x,y < 2^{30}).

Output

For each test case, output two non-negative integers pp and qq you found. If there are multiple pairs of pp and qq satisfying the conditions, you may output any of them.

It can be proven that under the constraints of the problem, any valid solution satisfies max(p,q)<231\max(p,q)<2^{31}.

1
2
3
4
5
6
7
8
7
0 0
1 1
3 6
7 11
4 4
123 321
1073741823 1073741822
1
2
3
4
5
6
7
0 0
2 1
3 8
6 9
4 3
128 321
1073741824 1073741822

Note

In the first test case, one valid pair would be p=0p=0 and q=0q=0, as 0&0=00\,\&\,0=0 and xp+yq=00+00=0|x-p|+|y-q|=|0-0|+|0-0|=0 is the minimum over all pairs of pp and qq.

In the third test case, one valid pair would be p=3p=3 and q=8q=8, as 3&8=03\,\&\,8=0 and xp+yq=33+86=2|x-p|+|y-q|=|3-3|+|8-6|=2 is the minimum over all pairs of pp and qq. Note that (p,q)=(3,4)(p,q)=(3,4) is also a valid pair.

思路讲解

我们需要利用这个高位较优的这个特性,

我们设计了一个 dp,dpi,0dp_{i,0} 表示该位采用 0,00,0 的配置,dpi,1dp_{i,1} 表示该位采用 1,01,0 的配置,dpi,2dp_{i,2} 表示该位采用 0,10,1 的配置。

不难写出这样的转移式子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vector dp(40,vector<Diff_p_q>(3));
dp[36][0]=Diff_p_q(0,0);
dp[36][1]=Diff_p_q(1ll<<36,0);
dp[36][2]=Diff_p_q(0,1ll<<36);
for (int i=35;i>=0;--i) {
dp[i]=dp[i+1];
// dp[i][0] 自己重新开始 继承 继承 继承
dp[i][0]=min({Diff_p_q(0,0),dp[i][0],dp[i+1][1],dp[i+1][2]});
// 自己重新开始
dp[i][1]=min({Diff_p_q(1ll<<i,0),Diff_p_q(dp[i+1][0].p+(1ll<<i),dp[i+1][0].q),
Diff_p_q(dp[i+1][1].p+(1ll<<i),dp[i+1][1].q),
Diff_p_q(dp[i+1][2].p+(1ll<<i),dp[i+1][2].q)});
dp[i][2]=min({Diff_p_q(0,1ll<<i),Diff_p_q(dp[i+1][0].p,dp[i+1][0].q+(1ll<<i)),
Diff_p_q(dp[i+1][1].p,dp[i+1][1].q+(1ll<<i)),
Diff_p_q(dp[i+1][2].p,dp[i+1][2].q+(1ll<<i))});
}

然后你忐忑地提交了,结果 WA on 2。

你使用了对拍,或者其实你不需要使用对拍,你也知道,是因为上面的做法有一些激进,高位上比较好,那自然是比较好的,但是好分大的好和小的好。那么上面的做法,大的好,自然是可以得到的,但是小的好,就不是那么简单的可以得到的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
_______________[ Testcase #1 INPUT ]_______________
1
78 88

_______________[ Testcase #1 OUTPUT]_______________
78 128
WA


[-] FAILURE: RUNTIME ERROR
Ans.diff:40
brute_diff:39
x_br:39
y_br:88

那么对拍也给了我们这个反馈,确实是这样的。

但是我们怎么样得到小的好呢?其实也简单,我们使用同样方法,整一个 dp,但是我们 dp 里存的的那个结构体的构造函数,我们传入一个指示参数,使得只要一出现大于的情况,就给他附上 diffINFdiff\gets\text{INF},使其不够优秀而被淘汰,强制得到这个我们所谓的小的好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Diff_p_q {
ll diff,p,q;
bool operator<(const Diff_p_q &o) const {
return diff<o.diff;
}
Diff_p_q()=default;
explicit Diff_p_q(ll p,ll q,bool no_big=false) {
if (!no_big) {
diff=abs(p-X)+abs(q-Y);
this->p=p;
this->q=q;
}else {
if (p > X || q > Y) {
diff=INF;
}else {
diff=abs(p-X)+abs(q-Y);
}
this->p=p;
this->q=q;
}

}
};

一样的 dp 过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Diff_p_q Ans=min({dp[0][0],dp[0][1],dp[0][2]});
{
vector dp2(40,vector<Diff_p_q>(3));
dp2[36][0]=Diff_p_q(0,0,true);
dp2[36][1]=Diff_p_q(1ll<<36,0,true);
dp2[36][2]=Diff_p_q(0,1ll<<36,true);
for (int i=35;i>=0;--i) {
dp2[i]=dp2[i+1];
// dp[i][0] 自己重新开始 继承 继承 继承
dp2[i][0]=min({Diff_p_q(0,0,true),dp2[i][0],dp2[i+1][1],dp2[i+1][2]});
// 自己重新开始
dp2[i][1]=min({Diff_p_q(1ll<<i,0,true),Diff_p_q(dp2[i+1][0].p+(1ll<<i),dp2[i+1][0].q,true),
Diff_p_q(dp2[i+1][1].p+(1ll<<i),dp2[i+1][1].q,true),
Diff_p_q(dp2[i+1][2].p+(1ll<<i),dp2[i+1][2].q,true)});
dp2[i][2]=min({Diff_p_q(0,1ll<<i,true),Diff_p_q(dp2[i+1][0].p,dp2[i+1][0].q+(1ll<<i),true),
Diff_p_q(dp2[i+1][1].p,dp2[i+1][1].q+(1ll<<i),true),
Diff_p_q(dp2[i+1][2].p,dp2[i+1][2].q+(1ll<<i),true)});
}
Ans=min({Ans,dp2[0][0],dp2[0][1],dp2[0][2]});
}
cout<<Ans.p<<" "<<Ans.q<<"\n";

okay,上面我们已经把我们的思路讲的比较清楚了。

我感觉这个代码对是因为,在确定好高位以后,绝对值距离最小的数一定贴在区间边界:要么贴上边界(低位全 0),要么贴下边界(低位全 1);中间形态永远不可能比边界更近。

不过我们的第一种 dp 由于没有引入这个 loose 和 tight,实际上计算出来的,并不是严格的 ,会有 << 的情况出现,所以我心里也不太踏实。欢迎 hack 我的代码。

AC代码

AC
https://codeforces.com/contest/2188/submission/360662700

心路历程(WA,TLE,MLE……)