题目链接
[SDOI2011] 消耗战
题目描述
在一场战争中,战场由 n n 个岛屿和 n − 1 n − 1 个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为 1 1 的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他 k k 个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到 1 1 号岛屿上)。不过侦查部门还发现了这台机器只能够使用 m m 次,所以我们只需要把每次任务完成即可。
输入格式
第一行一个整数 n n ,表示岛屿数量。
接下来 n − 1 n − 1 行,每行三个整数 u , v , w u , v , w ,表示 u u 号岛屿和 v v 号岛屿由一条代价为 w w 的桥梁直接相连。
第 n + 1 n + 1 行,一个整数 m m ,代表敌方机器能使用的次数。
接下来 m m 行,第 i i 行一个整数 k i k i ,代表第 i i 次后,有 k i k i 个岛屿资源丰富。接下来 k i k i 个整数 h 1 , h 2 , . . . , h k i h 1 , h 2 , . . . , h k i ,表示资源丰富岛屿的编号。
输出格式
输出共 m m 行,表示每次任务的最小代价。
样例 #1
样例输入 #1
样例输出 #1
提示
数据规模与约定
对于 10 % 10 % 的数据,n ≤ 10 , m ≤ 5 n ≤ 10 , m ≤ 5 。
对于 20 % 20 % 的数据,n ≤ 100 , m ≤ 100 , 1 ≤ k i ≤ 10 n ≤ 100 , m ≤ 100 , 1 ≤ k i ≤ 10 。
对于 40 % 40 % 的数据,n ≤ 1000 , 1 ≤ k i ≤ 15 n ≤ 1000 , 1 ≤ k i ≤ 15 。
对于 100 % 100 % 的数据,2 ≤ n ≤ 2.5 × 10 5 , 1 ≤ m ≤ 5 × 10 5 , ∑ k i ≤ 5 × 10 5 , 1 ≤ k i < n , h i ≠ 1 , 1 ≤ u , v ≤ n , 1 ≤ w ≤ 10 5 2 ≤ n ≤ 2.5 × 10 5 , 1 ≤ m ≤ 5 × 10 5 , ∑ k i ≤ 5 × 10 5 , 1 ≤ k i < n , h i ≠ 1 , 1 ≤ u , v ≤ n , 1 ≤ w ≤ 10 5 。
解题思路
虚树
考虑一般情况:
对于每次查询建立相应的虚树即可
代码
#include <bits/stdc++.h>
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int , int > PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax (T &x, T y) { return (y > x) ? x = y, 1 : 0 ; }
template <typename T> bool chkMin (T &x, T y) { return (y < x) ? x = y, 1 : 0 ; }
template <typename T> void inline read (T &x) {
int f = 1 ; x = 0 ; char s = getchar ();
while (s < '0' || s > '9' ) { if (s == '-' ) f = -1 ; s = getchar (); }
while (s <= '9' && s >= '0' ) x = x * 10 + (s ^ 48 ), s = getchar ();
x *= f;
}
const int N=2.5e5 +5 ;
int n,m,k,a[N],f[N][20 ],mn[N][20 ],dep[N],dfn[N],cnt,t,dis;
LL dp[N];
int stk[N],top;
vector<PII> adj[N];
bool v[N];
void dfs (int x,int fa)
{
dfn[x]=++cnt;
f[x][0 ]=fa;
for (int i=1 ;i<=t;i++)
f[x][i]=f[f[x][i-1 ]][i-1 ],mn[x][i]=min (mn[x][i-1 ],mn[f[x][i-1 ]][i-1 ]);
for (auto &t:adj[x])
{
int y=t.fi,w=t.se;
if (y==fa)continue ;
dep[y]=dep[x]+1 ;
mn[y][0 ]=w;
dfs (y,x);
}
}
int lca (int x,int y)
{
dis=1e9 ;
if (dep[x]>dep[y])swap (x,y);
for (int i=t;i>=0 ;i--)
if (dep[f[y][i]]>=dep[x])
{
dis=min (dis,mn[y][i]);
y=f[y][i];
}
if (x==y)return x;
for (int i=t;i>=0 ;i--)
if (f[x][i]!=f[y][i])
{
x=f[x][i],y=f[y][i];
dis=min ({dis,mn[x][i],mn[y][i]});
}
return f[x][0 ];
}
bool cmp (int &x,int &y)
{
return dfn[x]<dfn[y];
}
void build ()
{
sort (a+1 ,a+1 +k,cmp);
stk[top=1 ]=1 ,adj[1 ].clear ();
for (int i=1 ;i<=k;i++)
if (a[i]!=1 )
{
int l=lca (a[i],stk[top]);
if (l!=stk[top])
{
while (dfn[l]<dfn[stk[top-1 ]])
{
int u=stk[top-1 ],v=stk[top];
lca (u,v);
adj[u].pb ({v,dis});
top--;
}
if (dfn[l]>dfn[stk[top-1 ]])
{
adj[l].clear ();
lca (l,stk[top]);
adj[l].pb ({stk[top],dis});
stk[top]=l;
}
else
{
lca (l,stk[top]);
adj[l].pb ({stk[top--],dis});
}
}
adj[a[i]].clear (),stk[++top]=a[i];
}
for (int i=1 ;i<top;i++)
{
int u=stk[i],v=stk[i+1 ];
lca (u,v);
adj[u].pb ({v,dis});
}
}
void dfs (int x)
{
dp[x]=0 ;
for (auto &t:adj[x])
{
int y=t.fi,w=t.se;
dfs (y);
if (v[y])dp[x]+=w,v[y]=false ;
else
dp[x]+=min (1ll *w,dp[y]);
}
}
int main ()
{
help;
cin>>n;
t=__lg(n);
for (int i=1 ;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
adj[u].pb ({v,w}),adj[v].pb ({u,w});
}
dep[1 ]=1 ;
dfs (1 ,0 );
cin>>m;
while (m--)
{
cin>>k;
for (int i=1 ;i<=k;i++)cin>>a[i],v[a[i]]=true ;
build ();
dfs (1 );
cout<<dp[1 ]<<'\n' ;
}
return 0 ;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!