消耗战
题目链接:https://www.luogu.com.cn/problem/P2495
题解:
对于单样例,可以考虑树形DP。
但此题是多实例,所以需要对树进行处理,每次询问有k+1(加上一号点)个关键点。对每个询问构造出虚树。
边权:虚树的边上的权值,为原树中两点路径上的边权最小值,可以利用倍增的思想求权值。
DP:对于虚树,设
d
p
i
dp_i
dpi为以
i
i
i为根的子树都与i号节点断开连接的最小代价。对于
i
i
i的每个子节点
v
v
v,若v为关键点,则
d
p
i
+
=
d
i
s
(
i
,
v
)
dp_i+=dis(i,v)
dpi+=dis(i,v);若v不是关键点,则
d
p
i
+
=
m
i
n
(
d
i
s
(
i
,
v
)
,
d
p
v
)
dp_i+=min(dis(i,v), dp_v)
dpi+=min(dis(i,v),dpv)。
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<set>
#include<vector>
#include<queue>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-7
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 300100;
const int mod = 998244353;
int top, dep[maxn], fa[maxn][30], dis[maxn][30], a[maxn], vis[maxn], st[maxn];
int tot, cnt, dfn[maxn], hd1[maxn], hd2[maxn], nex[maxn*10], to[maxn*10], w[maxn*10];
void init(int n);
void Insert(int u);
LL solve(int u);
int LCA(int u, int v);
void add(int f, int t, int p, int hd[]);
void dfs(int u, int d, int f);
bool cmp(int a, int b);
int DIS(int u, int v);
int main()
{
tot = cnt = 1;
memset(hd1, -1, sizeof(hd1));
memset(hd2, -1, sizeof(hd2));
int t, n, i, j, k, q, m;
scanf("%d", &n);
for(i=1;i<n;i++){
int p;
scanf("%d %d %d", &j, &k, &p);
add(j, k, p, hd1);
add(k, j, p, hd1);
}
init(n);
scanf("%d", &q);
while(q--)
{
cnt = 0;
scanf("%d", &m);
for(i=0;i<m;i++){
scanf("%d", &a[i]);
vis[a[i]] = 1;
}
a[m++] = 1;
sort(a, a+m, cmp);
top = 0;
for(i=0;i<m;i++)
Insert(a[i]);
while(top>1){
add(st[top-1], st[top], DIS(st[top-1], st[top]), hd2);
top--;
}
LL ans = solve(1);
printf("%lld\n", ans);
}
return 0;
}
bool cmp(int a, int b)
{
return dfn[a]<dfn[b];
}
//构造虚树,
void Insert(int u)
{
if(top == 0){
st[++top] = u;
return;
}
int lca = LCA(u, st[top]);
while(top>1 && dep[lca]<dep[st[top-1]])
{
add(st[top-1], st[top], DIS(st[top-1], st[top]), hd2);
top--;
}
if(dep[lca]<dep[st[top]])add(lca, st[top--], DIS(lca, st[top]), hd2);
if((!top) || st[top]!=lca)st[++top] = lca;
st[++top] = u;
}
LL solve(int u)
{
LL mi, sum = 0;
for(int i=hd2[u];i!=-1;i=nex[i]){
mi = solve(to[i]);
if(vis[to[i]])
sum += w[i];
else
sum += min((LL)w[i], mi);
vis[to[i]] = 0;
}
hd2[u] = -1;
return sum;
}
int DIS(int u, int v)
{
if(dep[u]>dep[v])swap(u, v);
int k = dep[v]-dep[u], sum = INF;
for(int i=20;i>=0;i--)
if((k>>i) & 1){
sum = min(sum, dis[v][i]);
v = fa[v][i];
}
return sum;
}
void add(int f, int t, int p, int hd[])
{
to[++cnt] = t;
w[cnt] = p;
nex[cnt] = hd[f];
hd[f] = cnt;
}
void dfs(int u, int d, int f)
{
dep[u] = d;
fa[u][0] = f;
dfn[u] = tot++;
for(int i=hd1[u];i!=-1;i=nex[i])
if(to[i] != f){
dfs(to[i], d+1, u);
dis[to[i]][0] = w[i];
}
}
void init(int n)
{
dfs(1, n, 0);
for(int j=1;j<=22;j++)
for(int i=1;i<=n;i++){
fa[i][j] = fa[fa[i][j-1]][j-1];
dis[i][j] = min(dis[fa[i][j-1]][j-1], dis[i][j-1]);
}
}
int LCA(int u, int v)
{
if(dep[u] > dep[v])swap(u, v);
int i, j, k = dep[v]-dep[u];
for(i=20;i>=0;i--)
if(k&(1<<i))v = fa[v][i];
if(u == v)return u;
for(i=20;i>=0;i--)
if(fa[u][i] != fa[v][i])
v = fa[v][i], u = fa[u][i];
return fa[u][0];
}