问题描述
给定一棵树,每次从节点走到节点
,经过树上的边需要收费,对于每条边,可以选择价格为
的单程票或价格为
的多程票,求最小代价。
解题思路
1.基本思路
不难想到统计每条边经过次数,对于每条边比较两种方案代价,求和即可。
于是可以先求出每次将链上的边加权值加1后,每条边的权值。
2.暴力算法
每次深搜求链所有经过的边,将这些边权值加1。
时间复杂度 ,空间复杂度
。
3.树上差分
首先,对于每条边,将其权值记录在其连接的深度较大的节点中。
设,对于将链
所有边的权值加
,则需将链
边上权值加
,将
边上权值减
。
用到差分思想,每个节点维护值,使节点
权值等于子树
值之和。于是将
边上的值加
只需将节点的
值加
。
由于本题要求在所有修改后进行查询,所以可以直接维护值并在所有修改后求出所有节点权值。
时间复杂度,空间复杂度
。
4.DFS序实现
在DFS序中,节点的子树在一个区间内。在DFS过程中记录时间戳表示节点子树的区间。
用树状数组维护值,查询时只需求DFS序上子树表示区间之和。
时间复杂度,空间复杂度
。
示例代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5,M=N*2,K=40;
int n;
int a[N],b[N],c1[N],c2[N];
int la[N],ne[M],en[M],idx;
int dfi[N][2],dfi_;
int depth[N];
int fa[N][K];
int c[N];
int ans;
void add(int a,int b)
{
idx++;
ne[idx]=la[a];
la[a]=idx;
en[idx]=b;
}
void dfs(int u,int father)
{
dfi[u][0]=++dfi_;
fa[u][0]=father;
for(int k=1;k<K;k++)
{
fa[u][k]=fa[fa[u][k-1]][k-1];
if(!fa[u][k])break;
}
depth[u]=depth[father]+1;
for(int j=la[u];j;j=ne[j])
{
int v=en[j];
if(v!=father)dfs(v,u);
}
dfi[u][1]=dfi_;
}
int lca(int u,int v)
{
if(depth[u]>depth[v])swap(u,v);
for(int k=K-1;k>=0;k--)
if(depth[fa[v][k]]>=depth[u])v=fa[v][k];
if(u==v)return u;
for(int k=K-1;k>=0;k--)
if(fa[u][k]!=fa[v][k])
u=fa[u][k],v=fa[v][k];
return fa[u][0];
}
inline int lowbit(int x)
{
return x&-x;
}
void change(int x,int d)
{
for(;x<=n;x+=lowbit(x))
c[x]+=d;
}
int getsum(int x)
{
int s=0;
for(;x;x-=lowbit(x))
s+=c[x];
return s;
}
void modify_one(int u,int d)
{
change(dfi[u][0],d);
}
void modify(int u,int v)
{
int w=lca(u,v);
modify_one(u,1);
modify_one(v,1);
modify_one(w,-2);
}
int query(int u)
{
return getsum(dfi[u][1])-getsum(dfi[u][0]-1);
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<n;i++)
{
scanf("%lld%lld%lld%lld",&a[i],&b[i],&c1[i],&c2[i]);
add(a[i],b[i]);
add(b[i],a[i]);
}
dfs(1,0);
for(int i=1;i<n;i++)modify(i,i+1);
for(int i=1;i<n;i++)
{
int u=a[i],v=b[i];
if(fa[u][0]==v)ans+=min(c2[i],c1[i]*query(u));
else ans+=min(c2[i],c1[i]*query(v));
}
printf("%lld",ans);
return 0;
}
补充
这里再说下链修改维护节点权值的方法。
设,将链
上节点的权值加
,则需将链
点上权值加
,将
点上权值减
,若
不是根节点,将链
点上权值减
。
差分思想相同。