题目
题目链接
题意
在OxOx轴上有一堆点,这些点有三种类型R、B、PR、B、P型,现在要求添加一些线段把这些点连起来,使得如果去掉RR类型点,剩下的点都是联通的。如果去掉类型的点,剩下的点也是联通的,求最小的边长度总和。
题解
如果我们单单只看去掉R(B)R(B)类型的点的时候,这道题毫无疑问是一道最小生成树的题目,实际上也并不需要用一些最小生成树的做法来做,只需要按顺序扫一遍就好了。
但这个题不是,它要求这个图去掉R(B)R(B)类型的点之后,都仍旧是最小生成树。
我们称这张图为二树图(我胡起的,只是方便后面叙述,实际上并没有这个名字)。
那么如何构造这个二树图呢,我们采用归纳法。
我们从左向右扫描所得到的点,并且假设在所扫描过的点中已经过构造好了部分二树图。
那么对于我们当前正在扫描的点,该怎么进行处理呢?
当前的点是B(R)B(R)点,那么我们需要把这个B(R)B(R)点与前面最近的B(R)B(R)点或者最近的PP点相连接,这样的话保证了去掉点之后,当前的这个B(R)B(R)点连接到了生成树中,这样保持了二树图的性质。
当前的点是PP点,这种情况有点复杂,因为点是不会被去掉的,所以PP点影响了两种生成树。我们这样考虑,为了保证两个生成树都要连接到当前的这个点,对于去掉B(R)B(R)点得到的生成树,想要连接PP,那么我们一定要让这个连接到最近的R(B)R(B)或者PP点上,如果最近的是点,那么直接连接就好了,因为PP连接到了前面的点上,那么这个PP将同时连接到两颗生成树上去。
而如果到两颗树最近的点都不是PP点(即上一个与当前PP点之间即有点也有RR点这种情况),我们首先让当前的点连接最近的BB点和最近的点,在让当前的PP点与前一个点相连,这样的话,对于两颗生成树来说都产生了一个圈,为了保持生成树的性质,我们需要破圈。如果我们此时破了P−PP−P边,那么获得优势是cost(Pnow,Ppre)cost(Pnow,Ppre),保证是二树图,结束。如果我们不破P−PP−P边,那么我们势必要在两个圈中各找一个最大的Bx−Bx+1Bx−Bx+1和Ry−Ry+1Ry−Ry+1破掉,获得优势cost(Bx,Bx+1)+cost(Ry,Ry+1)cost(Bx,Bx+1)+cost(Ry,Ry+1),保证是二树图,结束。因此我们比较那个优势大就选择那种破法。
本题至此就解决了。
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 2e5+7;
typedef long long ll;
ll B,R,mxB,mxR,P,ans;
int n;
int main(){cin>>n;for(int i = 1;i <= n;++i){ll x;char c;scanf("%lld %c",&x,&c);x += 1e9+7;if(c == 'B'){if(B) ans += x - B,mxB = max(mxB,x - B);B = x; }if(c == 'R'){if(R) ans += x - R,mxR = max(mxR,x - R);R = x;}if(c == 'P'){if(B) mxB = max(mxB,x - B),ans += x-B;if(R) mxR = max(mxR,x - R),ans += x-R;if(P) {ans += min(0ll,x - P - mxB - mxR);}B = R = P = x;mxB = mxR = 0;}}cout<<ans<<endl;return 0;
}