您的位置:首页 > 其它

SPOJ 375 LCT学习

2015-09-10 23:07 190 查看
今天把这题的LCT补了一下:



下面是用树链剖分写的,上面使用LCT写的,树链剖分确实比LCT快,据说是LCT多一个常数的关系,但是这道题由于数据比较小的关系吧,树链剖分并没有比LCT快多少。

这道题的关键是一个边权转换成点权来写(话说如果这个不知道,那么树链剖分也写不出来了吧,但是我感觉是不是可以用map<pair<int,int>,int>来直接存边,然后做,好像这个样子又会多一个logn,没有尝试写过。。。),边权转化成点权在树链剖分中写起来很简单,其实LCT写起来也很简单,最主要是要找一个LCA,然后这个点的点权就不需要了,如果把这个点转化成root的话,那么我们需要查询的就是max(maxx[ch[root][0]],maxx[ch[root][1]]),那么我们现在要做的就是一个找LCA的过程,原来开始学LCT的时候是看着bin神代码学的,但是觉得他的LCA好像并没有什么用(果然还是太年轻)。LCA部分是学习bin神的,其实理解起来也比较简单,其实是两个Access过程,先Access(v),这个时候v到根节点的所有点形成了一个SPLAY,这个时候Access(u)这个过程中会找到v到根节点这棵SPLAY树(很容易理解,因为Access过程就是找到根的prefer-path,肯定会与前一个过程重合,说得再直白点,就是根节点一点是u,v的公共节点),然后我们发现这个时候把u转到根节点时,那么pre[u]
== 0了,这个时候找到的这个节点就是u,v的LCA,然后什么都解决了!!!其实还有一个点需要注意一下,就是Access过程已经把你需要PushDown的全部都PuhsDown了。

下面附上代码:

#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <vector>
#define LL long long
#define INF 0x3fffffff
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define MAXN 11000

using namespace std;

int n;

struct LCT{
int pre[MAXN],ch[MAXN][2],key[MAXN];
int maxx[MAXN],flip[MAXN];
bool rt[MAXN];

void Update_Flip(int x){
if(!x)  return;
swap(ch[x][0],ch[x][1]);
flip[x] ^= 1;
}

void Init(){
memset(ch,0,sizeof(ch));
memset(flip,0,sizeof(flip));
memset(rt,true,sizeof(rt));
key[0] = key[1] = maxx[0] = maxx[1] = -INF;
FOR(i,2,n+1)    maxx[i] = key[i];
}

void PushUp(int x){
maxx[x] = max(max(maxx[ch[x][1]],maxx[ch[x][0]]),key[x]);
}

void PushDown(int x){
if(flip[x]){
if(ch[x][0])    Update_Flip(ch[x][0]);
if(ch[x][1])    Update_Flip(ch[x][1]);
flip[x] = 0;
}
}

void Rotate(int x,int kind){
int y = pre[x];
PushDown(y);
PushDown(x);
ch[y][!kind] = ch[x][kind];
if(ch[x][kind]) pre[ch[x][kind]] = y;
if(rt[y]){
rt[x] = true;
rt[y] = false;
}
else{
if(ch[pre[y]][1] == y)  ch[pre[y]][1] = x;
if(ch[pre[y]][0] == y)  ch[pre[y]][0] = x;
}
pre[x] = pre[y];
pre[y] = x;
ch[x][kind] = y;
PushUp(y);
}

void Splay(int x){
PushDown(x);
while(!rt[x]){
int y = pre[x];
int z = pre[y];
if(rt[y]){
PushDown(y); PushDown(x);
Rotate(x,ch[y][0] == x);
}
else{
PushDown(z); PushDown(y); PushDown(x);
int kind = ch[z][0] == y;
if(ch[y][kind] == x){
Rotate(x,!kind);
Rotate(x,kind);
}
else{
Rotate(y,kind);
Rotate(x,kind);
}
}
}
PushUp(x);
}

void Access(int x){
int fa = 0;
for(;x;x = pre[fa = x]){
Splay(x);
rt[ch[x][1]] = true;
rt[ch[x][1] = fa] = false;
PushUp(x);
}
}

int GetRoot(int x){
Access(x);
Splay(x);
while(ch[x][0]) x = ch[x][0];
return x;
}

void MakeRoot(int x){
Access(x);
Splay(x);
Update_Flip(x);
}

void Modify(int x,int w){
Splay(x);
key[x] = w;
PushUp(x);
}
//LCA的过程,我只能说v = 0作为u的上一个点很关键!!!这个时候更新完了以后,u是原来的u,v的LCA,现在v是原来u->LVA(u,v)的部分,ch[u][1]对应的就是LVA(u,v)-    //>v的部分!!!
void Lca(int &u,int &v){
Access(v),v = 0;
while(u){
Splay(u);
if(!pre[u]) return;
rt[ch[u][1]] = true;
rt[ch[u][1] = v] = false;
PushUp(u);
u = pre[v = u];
}
}

int Query(int u,int v){
Lca(u,v);
return max(maxx[v],maxx[ch[u][1]]);
}

}lct;

struct Edge{
int u,v;
int idd,nt,w;
}edge[MAXN<<1];

int head[MAXN],edge_cnt,id[MAXN];

void add_edge(int u,int v,int idd,int w){
edge[edge_cnt].u = u;
edge[edge_cnt].v = v;
edge[edge_cnt].idd = idd;
edge[edge_cnt].w = w;
edge[edge_cnt].nt = head[u];
head[u] = edge_cnt++;
}

void dfs(int u,int fa){
lct.pre[u] = fa;
for(int i = head[u];i != -1;i = edge[i].nt){
int v = edge[i].v;
if(v == fa) continue;
lct.key[v] = edge[i].w;
id[edge[i].idd] = v;
dfs(v,u);
}
}

int main(){
//freopen("test.in","r",stdin);
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
memset(head,-1,sizeof(head));
edge_cnt = 0;
FOR(i,1,n){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,i,w);
add_edge(v,u,i,w);
}
dfs(1,0);
lct.Init();
char op[10];
while(~scanf("%s",op) && strcmp(op,"DONE")){
if(!strcmp(op,"CHANGE")){
int idd,w;
scanf("%d%d",&idd,&w);
lct.Modify(id[idd],w);
}
else{
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",lct.Query(u,v));
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: