spoj 2668
题目:http://www.spoj.com/problems/QTREE4/
题目大意:给你一棵有N个点的树,每个点有颜色,每条树枝有权值。先开始所有的点都是白色的。A 表示询问两个端点都为白色的最大距离(注意:两个点可以相同,即可以是同一个点),C a 表示将a点的颜色取反。
思路:漆子超论文中路径剖分与树的分治的联系的第一道例题,路径剖分的本质是基于链的分治算法,具体解题思路参看他的论文。
这道题写了一天,由于先开始一直没理解他论文里的意思,想了半天都想不通,对不上啊。后来突然之间就想通了,原来论文真的说的很清楚,他所指的树,以及向下走等的意思,均是对于将原树按照点到根的轻边个数分层摆放后的这棵树而言的。这一点想通了,他的思路就很清晰了。关键还在于怎么样在O(logN)维护存每个点的 d1、d2 值以及所有链的最大路径长度的这N+1个堆。这也是我在论文里最后不能理解的地方,反正关于这个实现还是想了半天。如果按照论文里,最直接的方法就是取d1、d2 的时候找一遍所有的 { maxl[ Li]+cost( ci ) }(由于是回溯,这时所有的子节点的maxl[ Li ] 都已经更新好了),但是这样一来,复杂度就肯定不行了,而且你既然每次都要重新找,那么也就不需要堆了。后来也是参看了别人的代码(发现这种题目看别人代码真的好累),原来只要每次更新完一个节点,只需要把这个maxl[ Li ],也就是rt == 1 的时候,push 进去它的 father 的那个堆。为了取出来的时候判断合法性,同时还需要把这棵线段树的根节点的编号 move + 1 (move 为每棵线段树的偏移量),以及cost (ci)传进去,如果取出来的时候,如果 top( ).d ! = maxl[ Li ]+cost(ci), 那么就是不合法的,就 pop( ) 掉。取d1、d2 的时候要先把d1pop()掉,再 push() 进来。另一个堆也是一样,把值传进去的时候,同时也要把这个值所对应的线段树的根节点编号传进去,用来判断是否合法。则询问的时候复杂度为O(1),修改为O(logN*logN)。
想了一天,又由于细节原因,WA了两个小时,最后才AC的。。 唉~,做这种题目真心伤身体。。 T ^ T
代码如下:
#include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<algorithm> using namespace std; const int INF = 0x0fffffff ; const int MAXN = 111111 ; struct Edge { int t,len,next; } edge[MAXN<<1]; int head[MAXN],tot; void add_edge(int s,int t,int len) { edge[tot].t=t; edge[tot].len=len; edge[tot].next = head[s]; head[s] = tot++; } int num[MAXN]; int son[MAXN]; int dis[MAXN]; void dfs(int u,int fa,int dist) { dis[u] = dist; num[u]=1; son[u]=-1; int maxv = 0; for(int e = head[u];e!=-1;e=edge[e].next) { int v = edge[e].t; int len = edge[e].len; if(v!=fa) { dfs(v,u,dist+len); if(num[v]>maxv) { maxv = num[v]; son[u] = v; } } } } int top[MAXN]; int w[MAXN],tot_w,f_w[MAXN]; struct Link { int l,r; } link[MAXN]; int first,tot_link; int father[MAXN],belong[MAXN]; void get_link(int u,int fa,int tp) { belong[u] = tot_link; father[u]=fa; w[u] = ++tot_w; f_w[tot_w] = u; top[u] = tp; if(first) { link[tot_link].l = tot_w; first=0; } if(son[u]!=-1) get_link(son[u],u,tp); else { link[tot_link].r=tot_w; tot_link++; first=1; } for(int e = head[u];e!=-1;e=edge[e].next) { int v = edge[e].t; if(v!=son[u]&&v!=fa) { get_link(v,u,v); } } } int cal_dis(int a,int b) { int x = dis[f_w[b]]-dis[f_w[a]]; return x; } int maxl[MAXN<<2],maxr[MAXN<<2],opt[MAXN<<2]; void push_up(int move,int l,int r,int m,int rt) { int ls = rt<<1,rs = rt<<1|1; maxl[move+rt] = max(maxl[move+ls],cal_dis(l,m+1)+maxl[move+rs]); maxr[move+rt] = max(maxr[move+rs],cal_dis(m,r)+maxr[move+ls]); opt[move+rt] = max(max(opt[move+ls],opt[move+rs]),cal_dis(m,m+1)+maxr[move+ls]+maxl[move+rs]); } struct D { int d,id,cost; D(int a,int b,int c) : d(a),id(b),cost(c) {}; bool operator < (const D &tmp) const { return d<tmp.d; } }; priority_queue <D> q[MAXN],all_link; int col[MAXN]; void update(int move,int l,int r,int rt,int pos,int u) { if(pos==l&&pos==r) { int d1 = -INF,d2 = -INF; int id1 = -1,cost1; while(!q[u].empty()) { int cur = q[u].top().d; int id = q[u].top().id; int len = q[u].top().cost; q[u].pop(); if(maxl[id]+len!=cur) continue; d1 = cur; id1 = id; cost1 = len; break; } while(!q[u].empty()) { int cur = q[u].top().d; int id = q[u].top().id; int len = q[u].top().cost; if(maxl[id]+len!=cur||id1==id) { q[u].pop(); continue; } d2 = cur; break; } if(id1!=-1) q[u].push(D(d1,id1,cost1)); if(col[u]==0) { maxl[move+rt] = maxr[move+rt] = max(d1,0); opt[rt+move] = max(0,max(d1,d1+d2)); } else { maxl[move+rt] = maxr[move+rt] = d1; opt[rt+move] = d1+d2; } //printf("u = %d,d1 = %d,d2 = %d\n",u,d1,d2); if(rt==1) { int x = top[u]; if(father[x]!=-1) { if(maxl[move+rt]>-INF) { int len = dis[x]-dis[father[x]]; q[father[x]].push(D(maxl[move+rt]+len,move+rt,len)); } } if(opt[move+rt]>-INF) all_link.push(D(opt[move+rt],move+rt,0)); } return ; } int m = l+r>>1; if(pos<=m) update(move,l,m,rt<<1,pos,u); else update(move,m+1,r,rt<<1|1,pos,u); push_up(move,l,r,m,rt); if(rt==1) { int x = top[u]; if(father[x]!=-1) { if(maxl[move+rt]>-INF) { int len = dis[x]-dis[father[x]]; q[father[x]].push(D(maxl[move+rt]+len,move+rt,len)); } } if(opt[move+rt]>-INF) all_link.push(D(opt[move+rt],move+rt,0)); } } void build(int u) { if(son[u]!=-1) build(son[u]); for(int e = head[u] ; e!=-1;e=edge[e].next) { int v= edge[e].t; if(v!=son[u]&&v!=father[u]) { build(v); } } int x = belong[u]; int move = (link[x].l-1)<<2; update(move,link[x].l,link[x].r,1,w[u],u); } void init() { memset(col,0,sizeof(col)); dfs(1,-1,0); tot_w=0; tot_link=1; link[1].l=1; get_link(1,-1,1); build(1); } int main() { int n; while(~scanf("%d",&n)) { int a,b,c; memset(head,-1,sizeof(head)); tot=0; for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); add_edge(a,b,c); add_edge(b,a,c); } for(int i=1;i<=n;i++) while(!q[i].empty()) q[i].pop(); while(!all_link.empty()) all_link.pop(); init(); int m; scanf("%d",&m); char str[5]; while(m--) { scanf("%s",str); if(str[0]=='A') { int have = 0; int ans; while(!all_link.empty()) { int cur = all_link.top().d; int id = all_link.top().id; if(opt[id]!=cur) { all_link.pop(); continue; } have = 1; ans = cur; break; } if(have) { printf("%d\n",ans); } else printf("They have disappeared.\n"); } else { int x; scanf("%d",&x); col[x]^=1; while(top[x]!=1) { int move = (link[belong[x]].l-1)<<2; update(move,link[belong[x]].l,link[belong[x]].r,1,w[x],x); x = father[top[x]]; } int move = (link[belong[x]].l-1)<<2; update(move,link[belong[x]].l,link[belong[x]].r,1,w[x],x); } } } return 0; }