2015多校1 Tree chain problem
之前那道Paths的升级版
在每一条链上加上了一定的权值
然后求最大值
其实就加了一个权值的条件
但这样一来题目基本上是完全不一样了
用贪心显然不行了
在树上求最大值可以试一下树形DP
定义dp[i]表示i的子树的最大值
如果要选用一条链更新一个根节点的信息
我们需要知道加上这条链的影响
首先加上这条链的权值以及这条链上点的dp值(假设这些dp值已经更新了)
定义sum[i]表示i的子节点的子树的dp值的和
然后就可以加上这条链上的sum值
但直接这样加我们会发现会将在这条链上除LCA外的点的dp值算进去
那么再减掉就好了
也就是:val + ∑sum[k] - ∑dp[k](k为链上的点)
注:LCA的DP值为0,不用直接加回去
这样就可以更新dp值了
但这样显然超时
得想办法把这个∑求和给消掉
再短时间内求出整条链的状态和
让我们回到一个简单点的问题:
如何求一条链上的权值和
其实这样的查询可以再O(1)的时间做到
首先预处理出根节点到所有点的路上的权值的和
再通过:根分别到两个节点的权值的和-根到LCA的值+LCA的值
就可以直接计算了
再到当前的问题
因为我们的树形DP是从深到浅的
所以当前节点以及当前节点到根的所有节点都是没有值的
所以只需要“根分别到两个节点的权值的和”这一步
注意:再向上更新的过程中,路径上的点的权值会改变
所以需要不断维护当前点的子树上的所有点
于是我们需要一种能在短时间内查询和区间更新的数据结构
那么。。线段树(或是树状数组)
直接用线段树显然不行,一棵子树上的点不一定连续
那么再引入DFS序,记录一段子树的区间
这样就可以用线段树查询了
使用线段树后,上面的状态转移也要发生变化
因为在更新的时候,当前的sum值是还未更新的,所以手动加上
dp[i]=max(sum[i],sum[i]+val+线段树查询的sum的和-线段树查询的dp的和)
注意:更新一个dp值后要更新线段树
#include
#include
#include
#include
#include
using namespace std;
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define INF 0x3f3f3f3f
#define M 100005
#define N 20
void chkmx(int &a,int b) {if(avoid chkmi(int &a,int b) {if(a>b)a=b;}
int MAX(int a,int b) {return a>b?a:b;}
int MIN(int a,int b) {return astruct edge {int fr,to,v;};
vector G[M];
vector<int> es[M];
int n,m,T,a,b,c;
int dep[M],Fa[M][N],Lt[M],Rt[M];
int dp[M],sum[M];
struct node {
int dp,sum;
node operator +(const node &a)const {
node tmp;
tmp.dp=dp+a.dp;
tmp.sum=sum+a.sum;
return tmp;
}
};
struct Get_Dfs {
void dfs(int s,int fr) {
Lt[s]=++T;
dep[s]=dep[fr]+1,Fa[s][0]=fr;
FOR(i,0,es[s].size()-1) {
int y=es[s][i];
if(y==fr) continue;
dfs(y,s);
}
Rt[s]=T;
}
void solve() {
T=0;
dfs(1,0);
}
} Get_Dfs;
struct LCA {
void Init() {
FOR(j,1,N-1)FOR(i,1,n)
Fa[i][j]=Fa[Fa[i][j-1]][j-1];
}
void up(int &s,int step) {
FOR(i,0,N-1) {
if((1<int solve(int a,int b) {
if(dep[a]>dep[b]) swap(a,b);
up(b,dep[b]-dep[a]);
if(a==b) return a;
DOR(i,N-1,0) {
if(Fa[a][i]!=Fa[b][i])
a=Fa[a][i],b=Fa[b][i];
}
return Fa[a][0];
}
}LCA;
struct Segment_Tree {
struct W {
int L,R;
node x;
} tree[M<<2];
void build(int L,int R,int p) {
tree[p].L=L,tree[p].R=R;
tree[p].x.dp=0;
tree[p].x.sum=0;
if(L==R) return ;
int mid=L+R>>1;
build(L,mid,p<<1);
build(mid+1,R,p<<1|1);
}
node query(int x,int p) {
int L=tree[p].L,R=tree[p].R;
if(L==R) return tree[p].x;
int mid=L+R>>1;
if(x<=mid) return tree[p].x+query(x,p<<1);
else return tree[p].x+query(x,p<<1|1);
}
void update(int L,int R,node a,int p) {
if(L==tree[p].L&&R==tree[p].R) {
tree[p].x=tree[p].x+a;
return ;
}
int mid=tree[p].L+tree[p].R>>1;
if(R<=mid) update(L,R,a,p<<1);
else if(L>mid) update(L,R,a,p<<1|1);
else update(L,mid,a,p<<1),update(mid+1,R,a,p<<1|1);
}
}Tree;
struct Tree_DP{
void Init(){
FOR(i,1,m) {
scanf("%d%d%d",&a,&b,&c);
G[LCA.solve(a,b)].push_back((edge){a,b,c});
}
}
void dfs(int s,int fr) {
FOR(i,0,es[s].size()-1) {
int y=es[s][i];
if(y==fr)continue;
dfs(y,s);
sum[s]+=dp[y];
}
dp[s]=sum[s];
FOR(i,0,G[s].size()-1) {
int fr=G[s][i].fr,to=G[s][i].to;
node a=Tree.query(Lt[fr],1);
node b=Tree.query(Lt[to],1);
chkmx(dp[s],sum[s]+G[s][i].v+a.sum+b.sum-a.dp-b.dp);
}
Tree.update(Lt[s],Rt[s],(node){dp[s],sum[s]},1);
}
void solve(){
Init();
dfs(1,0);
}
}Tree_DP;
struct Solve {
void Input() {
cin>>n>>m;
FOR(i,2,n) {
scanf("%d %d",&a,&b);
es[a].push_back(b);
es[b].push_back(a);
}
}
void Work() {
Get_Dfs.solve();
LCA.Init();
Tree.build(1,n,1);
Tree_DP.solve();
}
void Print(){
printf("%d\n",dp[1]);
}
void Clear() {
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
FOR(i,1,n) es[i].clear();
FOR(i,1,n) G[i].clear();
}
} Solve;
int main() {
int q;
cin>>q;
while(q--) {
Solve.Input();
Solve.Work();
Solve.Print();
Solve.Clear();
}
return 0;
}