今天把这题的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 ",lct.Query(u,v)); } } } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。