spoj 2666 (树链的分治)

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;
}


你可能感兴趣的:(spoj 2666 (树链的分治))