SPOJ Query on a tree

题意:给出一个n个节点的树,边的标号分别是1-n-1,然后有两种操作,CHANGE 将第i条边的权值改成ti,QUERY 查询a点到b点中的边的最大权值。。

QTREE系列的第一题,第一次写树链剖分,树链剖分之后用线段树解决,中途出现了RE和WA,找了几天才解决,都在代码中注释了。。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<limits.h>
#define ls (k<<1)
#define rs (k<<1|1)
using namespace std;
const int MAXN=10010;
const int INF=INT_MAX;
struct EDGE
{
    int v,next;
}edge[MAXN<<1];
int head[MAXN],size;
int totw;
int fa[MAXN],dep[MAXN],siz[MAXN],son[MAXN],w[MAXN],top[MAXN];
void init() //初始化
{
    memset(head,-1,sizeof(head));
    memset(dep,0,sizeof(dep));
    memset(siz,0,sizeof(siz));
    memset(son,-1,sizeof(son));
    memset(fa,-1,sizeof(fa));
    memset(top,0,sizeof(top));
    size=0;
    totw=0;
}
void add_edge(int u,int v)
{
    edge[size].v=v;
    edge[size].next=head[u];
    head[u]=size++;
}
void dfs1(int u,int f,int deep)
{
    siz[u]=1;	//自己算1
    int mmax=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
	int v=edge[i].v;
	if(v!=f)
	{
	    fa[v]=u;
	    dep[v]=dep[u]+1;
	    dfs1(v,u,deep+1);
	    siz[u]+=siz[v];
	    if(siz[v]>mmax)//找出最大的子节点
	    {
		mmax=siz[v];
		son[u]=v; //重孩子
	    }
	}
    }
}
void dfs2(int u,int f)	//f表示一条重链的顶端
{
    top[u]=f;	//重链上的点都指向最高的点
    totw++;
    w[u]=totw;	//标号先标记重链上的边
    //printf("2222\n");
    if(son[u]!=-1)
	dfs2(son[u],f);	//沿重链深搜下去,保证先标号
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
	int v=edge[i].v;
	if(v!=fa[u]&&v!=son[u])
	    dfs2(v,v);	//轻链
    }
}
struct TREE
{
    int l,r;
    int mmax;
    int mid()
    {
	return (l+r)>>1;
    }
}tree[MAXN<<2];
struct LINE
{
    int x,y,c;
}l[MAXN];
int val[MAXN];
void pushup(int k)
{
    tree[k].mmax=max(tree[ls].mmax,tree[rs].mmax);
}
void build(int l,int r,int k)
{
    tree[k].l=l;
    tree[k].r=r;
    if(l==r)
    {
	tree[k].mmax=val[l];
	return;
    }
    int mid=tree[k].mid();
    build(l,mid,ls);
    build(mid+1,r,rs);
    pushup(k);
}
void update(int va,int pos,int k)
{
    if(tree[k].l==tree[k].r)
    {
	tree[k].mmax=va;
	return;
    }
    int mid=tree[k].mid();
    if(pos<=mid)
	update(va,pos,ls);
    else
	update(va,pos,rs);
    pushup(k);
}
int query(int l,int r,int k)
{
    if(tree[k].l>=l&&tree[k].r<=r)
    {
	return tree[k].mmax;
    }
    int mid=tree[k].mid();
    if(r<=mid)
	return query(l,r,ls);
    else if(l>mid)
	return query(l,r,rs);
    else
    {
	int t1=query(l,mid,ls);
	int t2=query(mid+1,r,rs);
	return max(t1,t2);
    }
}
void update1(int x,int y,int val)
{
    while(top[x]!=top[y])
    {
	if(dep[top[x]]<dep[top[y]]) //这里应该是dep,不是top
	    swap(x,y);
	update(val,w[x],1);
	x=fa[top[x]];
    }
    if(x==y)
	return;
    if(dep[x]<dep[y])
	swap(x,y);
    update(val,w[x],1);
    //printf("val=%d\n",val);
}
int query1(int x,int y)
{
    int ans=-INF;
    while(top[x]!=top[y])
    {
	if(dep[top[x]]<dep[top[y]])
	    swap(x,y);
	ans=max(ans,query(w[top[x]],w[x],1));
	//printf("ans=%d\n",ans);
	x=fa[top[x]];
    }
    if(x==y)
	return ans;
    if(dep[x]>dep[y]) //写成top导致RE
	swap(x,y);
    return max(ans,query(w[x]+1,w[y],1));
}
char str[20];
int main()
{
    int t,n,i;
    //freopen("in.txt","r",stdin);
    scanf("%d",&t);
    while(t--)
    {
	scanf("%d",&n);
	init();
	int u,v,c;
	for(i=1;i<n;i++)
	{
	    scanf("%d%d%d",&u,&v,&c);
	    l[i].x=u;
	    l[i].y=v;
	    l[i].c=c;
	    add_edge(u,v);
	    add_edge(v,u);
	    //printf("XXXX1\n");
	}
	totw=0;
	dfs1(1,-1,0);
	//printf("11111\n");
	dfs2(1,1);
	for(i=1;i<n;i++)
	{
	    int a=l[i].x,b=l[i].y;
	    if(dep[a]<dep[b])
		swap(a,b);
	    val[w[a]]=l[i].c;
	    //printf("XXXX2\n");
	}
	build(1,totw,1);
	while(scanf("%s",str)!=EOF)
	{
	    if(str[0]=='D')
		break;
	    int x,y;
	    scanf("%d%d",&x,&y);
	    if(str[0]=='Q')
		printf("%d\n",query1(x,y));
	    else
	    {
		//printf("i=%d c=%d\n",i,l[i].c);
		int a=l[x].x,b=l[x].y;
		update1(a,b,y);
	    }
	}
	if(t)
	    printf("\n");
    }
    return 0;
}
/*
2
3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 -100
QUERY 1 2
DONE
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值