消耗战
初步学习虚树的学习笔记。
当时听说要学虚树还以为要学虚数成功被吓得不轻。不过还好,第一道题(这道题)调的比较顺利,但到第二道题也就是世界树时就回到了以前那种几个小时都调不出来的抓狂阶段(到现在也没搞出来),就只能小小地总结一下……
虚树是什么?教材上说的是一种解题技巧而非算法,更不是什么数据结构。它运用了均摊的思想,把N次询问每次询问不超过M个节点但\(\sum M\)有限制之类的问题给均摊到了\(O(\sum)\)的程度而非朴素算法的近乎\(O(M\times N)\)。至于它是怎么做到的,它用的思想就是把询问提到的那些点拎出来组成一棵树,在这棵树上进行操作(比如DP),而新造的这棵树就被称为虚树。
模板题都不算太难。
#include<cstdio>
#include<cstring>
#include<algorithm>
//#define zczc
#define int long long
#define ll long long
using namespace std;
const int N=300010;
const int S=30;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int min(int s1,int s2){
return s1<s2?s1:s2;
}
inline ll minl(ll s1,ll s2){
return s1<s2?s1:s2;
}
int m;
struct edge{
int t,v,nxt;
}e[N<<1];
int head[N],esum;
inline void add(int fr,int to,int val){
//printf("e:%d %d\n",fr,to);
esum++;
e[esum].t=to;
e[esum].v=val;
e[esum].nxt=head[fr];
head[fr]=esum;
return;
}
int nxt[N][S],dfn[N],d[N],lg[N],tot,minn[N];
void dfs(int wh,int fa){
dfn[wh]=++tot;
nxt[wh][0]=fa;
d[wh]=d[fa]+1;
for(int i=1;i<=lg[d[wh]];i++){
nxt[wh][i]=nxt[nxt[wh][i-1]][i-1];
}
for(int i=head[wh],th;i;i=e[i].nxt){
th=e[i].t;
if(th==fa)continue;
minn[th]=min(minn[wh],e[i].v);
dfs(th,wh);
}
return;
}
inline void swap(int &s1,int &s2){
int s3=s1;s1=s2;s2=s3;return;
}
int lca(int s1,int s2){
//printf("ask for:%d %d,",s1,s2);
if(d[s1]<d[s2])swap(s1,s2);
for(int i=lg[d[s1]];i>=0;i--){
if(d[nxt[s1][i]]>=d[s2]){
s1=nxt[s1][i];
}
}
if(s1==s2){
//printf("ans is %d\n",s1);
return s1;
}
for(int i=lg[d[s1]];i>=0;i--){
if(nxt[s1][i]!=nxt[s2][i]){
s1=nxt[s1][i];
s2=nxt[s2][i];
}
}
//printf("ans is %d\n",nxt[s1][0]);
return nxt[s1][0];
}
int s[N],top;
bool in[N];
int n,num,a[N];
inline bool cmp(int s1,int s2){
return dfn[s1]<dfn[s2];
}
ll f[N];
void work(int wh,int fa){
ll an=0;
for(int i=head[wh],th;i;i=e[i].nxt){
th=e[i].t;
if(th==fa)continue;
work(th,wh);
an+=f[th];
}
if(in[wh])f[wh]=minn[wh];
else f[wh]=minl(minn[wh],an);
//printf("dp:%d %d %d %d\n",wh,fa,f[wh],in[wh]);
in[wh]=false;
head[wh]=0;
return;
}
void solve(){
read(num);
for(int i=1;i<=num;i++){
read(a[i]);
in[a[i]]=true;
}
//printf("\n\nss:\n");
//for(int i=1;i<=num;i++)printf("%d ",a[i]);
//printf("\n");
sort(a+1,a+num+1,cmp);
//printf("working");
//build
esum=top=0;
s[++top]=1;
for(int i=1;i<=num;i++){
//printf("now:%d\n",i);
int wh=a[i],l=lca(wh,s[top]);
while(top>1){
if(d[l]<d[s[top-1]]){
//add(s[top],s[top-1],0);
add(s[top-1],s[top],0);
top--;
}
else{
if(d[l]<d[s[top]]){
//add(s[top],l,0);
add(l,s[top],0);
top--;
}
break;
}
}
if(l!=s[top])s[++top]=l;
if(wh!=s[top])s[++top]=wh;
}
//printf("working");
while(top>1){
//add(s[top],s[top-1],0);
add(s[top-1],s[top],0);
top--;
}
work(1,0);
printf("%lld\n",f[1]);
return;
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
lg[0]=1;
for(int i=1;i<N;i++)lg[i]=lg[i>>1]+1;
int s1,s2,s3;
read(m);
for(int i=1;i<m;i++){
read(s1);read(s2);read(s3);
add(s1,s2,s3);add(s2,s1,s3);
}
memset(minn,0x3f,sizeof(minn));
dfs(1,0);
memset(head,0,sizeof(head));
esum=0;
//for(int i=1;i<=m;i++){
// printf("rr:%d\n",dfn[i]);
//}
read(n);
while(n--){
//printf("n=%d\n",n);
solve();
}
return 0;
}
一如既往,万事胜意