模拟8 题解
A. 匹配
$Hash$直接搞。
如果使用$KMP$,注意B字符串与A完全匹配,这是原本的$KMP$无法处理的问题。
B. 回家
刚开始以为是割点,打完后仔细一想发现不对。
于是打了圆方树,没有什么技巧。
另一种简单的做法:
只判断割点,但修改判断割点的方法。
1 void tarjan(int x) 2 { 3 int son=0; 4 dfn[x]=low[x]=++num; 5 for(register int i=fr[x];i;i=mo[i].pr) 6 { 7 register int y=mo[i].to; 8 if(!dfn[y]) 9 { 10 tarjan(y); 11 low[x]=min(low[x],low[y]); 12 if(low[y]>=dfn[x]&&dfn[y]<=dfn[n]) 13 { 14 son++; 15 if(x!=1||son>1) cut[x]=1; 16 } 17 } 18 else low[x]=min(low[x],dfn[y]); 19 } 20 }
C. 寿司
断环成链,即把1~n-1补充在长度为n的字符串后。
将问题中的R和B转化为0和1,
问题转化为取一个长度为n的区间,移动1,使全部1分布在区间的左右两侧。
不难发现1每次都应与0交换,才能保证结果最优。
如果1向左移,移动的距离 等于 与左端点的距离 减去 两者中间1的个数。
利用前缀和优化,于是能推出$O(1)$计算的式子。
可以$O(n^2)$枚举区间和1左移右移的分界线,取最小值。
关于优化枚举的过程:
不难想到对于每个区间,函数值对于分界线是单谷的。(证明略去,可以参考O(n)正解的证明)
使用三分法求单峰函数极值即可。
注意在整数域上,三分法只能将极值收缩到长度不超过3的区间,都应尝试更新。
复杂度$O(nlogn)$ 常数稍大。
又发现随着区间的右移,最优决策点是单调不左移的。
而且最优决策点是一个定值,设区间内0的个数为t,
如果t为奇数,最优决策点在中位0的左右。
如果t为偶数,最优决策点在中位两个0左右。
证明:(大神yxs)
每个1的 向两侧移动的距离 等同于 1与左右端点之间 0的个数。
对于每一个1,当它在中位0左侧,去左端点更优,
反之则去右端点更优。
因为区间中0的总个数不变。
当区间右移,
如果原左端点为1,中位0不变。
如果原左端点为0,中位0右移。
故中位0单调变化,以一个指针记录中位0的位置,用$while$判断是否右移即可。
不断更新答案,总复杂度$O(n)$