FZU - 2082 过路费

这篇文章讨论了在链表竞赛题目中,如何通过树链剖分技巧处理边权问题,特别强调了在使用线段树或树状数组维护dfs序时,需注意数据类型导致的运行时错误。重点在于理解为何将边权转换为点权操作可能导致错误,并提供了题解和代码示例。
摘要由CSDN通过智能技术生成

其实呢,这题很简单,树链剖分的板子题,基本不会错,就算错误大概是RE(runtime error),为什么会这么说呢?你如果判断当前是什么操作的op是longlong类型,那恭喜你喜提一发RE,只有int才能够刚刚好过去,很神奇,很奇妙,百思不得其解。
题解:将树重链剖分后,然后用线段树或树状数组等结构维护得到的dfs序即可,由于权为边权而非点权,那么只要将边权设两点间深度较深的那点为点权即可将边权转为点权

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<ctime>
#include<algorithm>
#include<sstream>
#include<bitset>
#define scand(a) scanf("%d",&a)
#define scandd(a,b) scanf("%d%d",&a,&b)
#define scanddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define mst(a,b) memset(a,b,sizeof(a))
#define lowbit(x) (x&-x)
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3fLL;
const int maxn=1e5+5;
const ll mod=1e9+7;
struct nod
{
	int nxt,to;
}edge[maxn<<1];
int head[maxn],cnt=1;
void add(int u,int v)
{
	edge[++cnt].nxt=head[u];
	edge[cnt].to=v;
	head[u]=cnt;
}
int f[maxn],dep[maxn],size[maxn],son[maxn],rnk[maxn],tp[maxn],id[maxn],tot,rt,n,m;
ll uu[maxn],vv[maxn],ww[maxn];
ll tre[maxn<<2];
void upd(int rt,int l,int r,int pos,ll w)
{
	if(l==r)
	{
		tre[rt]=w;
		return;
	}
	int mid=l+r>>1;
	if(pos<=mid)upd(lson,l,mid,pos,w);
	else upd(rson,mid+1,r,pos,w);
	tre[rt]=tre[lson]+tre[rson];
}
void dfs1(int u,int fa,int depth)
{
	f[u]=fa;
	size[u]=1;
	dep[u]=depth;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==fa)continue;
		dfs1(v,u,depth+1);
		size[u]+=size[v];
		if(size[v]>size[son[u]])son[u]=v;
	}
}
void dfs2(int u,int t)
{
	tp[u]=t;
	id[u]=++tot;
	if(son[u])dfs2(son[u],t);
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v!=son[u]&&v!=f[u])dfs2(v,v);
	}
}
ll que(int rt,int l,int r,int pl,int pr)
{
	if(pl<=l&&r<=pr)
	{
		return tre[rt];
	}
	ll sum=0;
	int mid=l+r>>1;
	if(pl<=mid)sum+=que(lson,l,mid,pl,pr);
	if(pr>mid)sum+=que(rson,mid+1,r,pl,pr);
	return sum;
}
void ans(int x,int y)
{
	ll sum=0;
	while(tp[x]!=tp[y])
	{
		if(dep[tp[x]]<dep[tp[y]])swap(x,y);
		sum+=que(1,1,n,id[tp[x]],id[x]);
		x=f[tp[x]];
	}
	if(id[x]>id[y])swap(x,y);
	if(x!=y) //由于是问的两点间的路费,显然同一点是不用路费的
	sum+=que(1,1,n,id[son[x]],id[y]);
	/*
	因为边权转点权,所以如果用的是id[x]的话,显然会多算一段边权进来
	*/
	printf("%lld\n",sum);
}
int main()
{
    #ifdef local
    freopen("1.txt","r",stdin);
//    freopen("2.txt","w",stdout);
    #endif
    while(~scandd(n,m))
    {
    	cnt=1,tot=0;
    	mst(tre,0);
    	mst(head,0);
    	mst(son,0);
    	mst(dep,0);
    	mst(edge,0);
        for(int i=1;i<=n-1;i++)
	    {
	    	scanf("%lld%lld%lld",&uu[i],&vv[i],&ww[i]);
	    	add(uu[i],vv[i]);
	    	add(vv[i],uu[i]);
		}
		dfs1(1,0,1);
		dfs2(1,1);
		for(int i=1;i<=n-1;i++)
		{
			if(dep[uu[i]]<dep[vv[i]])swap(uu[i],vv[i]);
			upd(1,1,n,id[uu[i]],ww[i]);
		}
		for(int i=0;i<m;i++)
		{
			int op;ll a,b;
			scanf("%d%lld%lld",&op,&a,&b);
			if(op==0)
			{
				upd(1,1,n,id[uu[a]],b);
			}
			else ans(a,b);
		}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值