题目大意:找出两点间一条路径,使路径上最大的边最小,含加边操作
改悔!
比赛的时候看到这道题(读题读了很久),就想到了看到过的WC2006水管局长那道题,这道题数据还要水一些。
做法就是维护MST,用LCT加边,加边的时候找一下两个点间最长的一条边,如果比要加的边长,就删掉这条边然后加边,否则不加边
然而我用了一个想当然的写法,导致样例都出不了
LCT只能维护点权,但MST需要维护的是边权,就需要把边权转化为点权
考虑到以前树剖的写法,我把树上一条边的权值加到深的点上,这样除了根节点,每个点的权值表示它到其父节点的边权
然而LCT如果也这么搞,那就GG了
原因是LCT中link、cut都需要换根操作,换根之后,部分节点的父子关系被调换了,而我们也无法将经过如上处理的点权下放,所以LCT维护边权肯定不能这么搞的(除非你不用link和cut)
那么就只能用笨一点的方法了,我们把每条边看作一个点,该点与原边的两端点连边,它的权值就是边的权值,这样原有结构就不会被lct操作破坏了
代码:
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200005,M=N<<2;
//inline int _R(){
// int d=0;bool b=1;char t=getchar();
// while(t<'0'||t>'9'){if(t=='-')b=0;t=getchar();}
// for(;t>='0'&&t<='9';t=getchar())d=(d<<3)+(d<<1)+t-'0';
// return b?d:-d;
//}
int n,m,T;
ll Val[M];
struct Edge{
int x,y,id;
ll z;
}E[M];
bool cmp1(Edge a,Edge b){return a.z<b.z;}
bool cmp2(Edge a,Edge b){return a.id<b.id;}
//LCT
int Son[N][2],Fa[N],Max[N];
bool Rev[N];
inline bool IsRoot(int x){
return Son[Fa[x]][0]!=x&&Son[Fa[x]][1]!=x;
}
inline void PushUp(int x){
int l=Son[x][0],r=Son[x][1];
Max[x]=x;
if(Val[Max[l]]>Val[Max[x]])Max[x]=Max[l];
if(Val[Max[r]]>Val[Max[x]])Max[x]=Max[r];
}
inline void PushDown(int x){
if(Rev[x]){
Rev[x]=0;
Rev[Son[x][0]]^=1;
Rev[Son[x][1]]^=1;
swap(Son[x][0],Son[x][1]);
}
}
inline void Rotate(int x){
int y=Fa[x],z=Fa[y],l=Son[y][0]==x,r=l^1;
if(!IsRoot(y))Son[z][Son[z][1]==y]=x;
Fa[x]=z,Fa[Son[x][l]]=y,Son[y][r]=Son[x][l];
Son[x][l]=y,Fa[y]=x;
PushUp(y),PushUp(x);
}
int s[N],top;
void Splay(int x){
s[top=1]=x;
for(int i=x;!IsRoot(i);i=Fa[i])s[++top]=Fa[i];
while(top)PushDown(s[top--]);
while(!IsRoot(x)){
int y=Fa[x],z=Fa[y];
if(!IsRoot(y)){
if((Son[y][0]==x)==(Son[z][0]==y))Rotate(y);
else Rotate(x);
}
else Rotate(x);
}
}
void Access(int x){
int t=0;
while(x){
Splay(x);
Son[x][1]=t;
PushUp(x);
t=x;x=Fa[x];
}
}
void MakeToRoot(int x){
Access(x),Splay(x),Rev[x]^=1;
}
void Link(int x,int y){
MakeToRoot(x);
Fa[x]=y;
Splay(x);
}
void Cut(int x,int y){
MakeToRoot(x);
Access(y),Splay(y);
Son[y][0]=Fa[x]=0;
}
int Query(int x,int y){
MakeToRoot(x);
Access(y),Splay(y);
return Max[y];
}
//Kruscal,Build LCT
int KFa[N];
int GetFa(int x){return x==KFa[x]?x:KFa[x]=GetFa(KFa[x]);}
void Kruscal(){
sort(E+1,E+1+m,cmp1);
int x,y,fx,fy,cnt=0;
for(int i=1;i<=n;i++)KFa[i]=i;
for(int i=1;i<=m&&cnt<n-1;i++){
x=E[i].x,y=E[i].y;
fx=GetFa(x),fy=GetFa(y);
if(fx!=fy){
KFa[fx]=fy;
Link(x,E[i].id+n);
Link(y,E[i].id+n);
cnt++;
}
}
}
int main(){
// freopen("game19.in","r",stdin);
// freopen("game.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%lld",&E[i].x,&E[i].y,&E[i].z);
E[i].id=i;
Val[i+n]=E[i].z;
Max[i+n]=i+n;
}
Kruscal();
scanf("%d",&T);
char str[20];
int x,y,a,b;
ll z;
sort(E+1,E+1+m,cmp2);
while(T--){
scanf("%s",str);
if(str[0]=='g'){
scanf("%d%d%d%d",&x,&y,&a,&b);
if(Val[Query(x,y)]==Val[Query(a,b)])puts("Baozika");
else puts("madoka");
}
if(str[0]=='a'){
scanf("%d%d%lld",&x,&y,&z);
b=Query(x,y);
if(Val[b]>z){
E[++m].x=x,E[m].y=y,E[m].id=m,E[m].z=z;
Val[m+n]=z;
Cut(E[b-n].x,b),Cut(E[b-n].y,b);
Link(x,m+n),Link(y,m+n);
}
}
}
}