BZOJ 2286: [Sdoi2011]消耗战 虚树 树形dp 动态规划 dfs序
https://www.lydsy.com/JudgeOnline/problem.php?id=2286
wa了两次因为lca犯了zz错误
这道题如果不多次询问的话就是裸dp。
一棵树上多次询问,且总共询问的点数较小(能够承受得住加个logn的复杂度(常数就不管了,按理说还要*2吧))可以用虚树来处理。
虚树就是对每次询问将有用的点建一棵树,每次询问查询m个点,则这棵树最多m*2个点(太优秀了)。
这个虚树的建立过程是用栈维护一条链,每加入一个点就把她和前一条链的叶子节点的lca和这个点本身加入虚树中,然后将当前链的叶子节点改为当前加入的点。
dfs序是个好东西,它帮助维护了这条链和后面点的关系(的逻辑性?(看到代码就懂了吧,反正就很有逻辑就对了))。
所以我之前为什么要学树链剖分,树链剖分什么用都没有嘤嘤嘤,最喜欢倍增了。(日常抛弃旧爱)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<map> 7 using namespace std; 8 #define LL long long 9 const int maxn=250010; 10 const LL minf=(LL)5e17; 11 int n,m; 12 struct nod{ 13 int nex,y,v; 14 };nod e[maxn*2];nod e1[maxn*4]; 15 int head[maxn]={},tot=0; 16 int head1[maxn]={},tot1=0; 17 int id[maxn]={},cnt=0; 18 int fa[maxn][22]={},dep[maxn]={}; 19 LL dis[maxn]={},f[maxn]={}; 20 int a[maxn]={},tly=0; 21 int sta[maxn]={},tai=0; 22 inline int mread(){ 23 int x=0,f=1;char ch=getchar(); 24 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 25 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 26 return x*f; 27 } 28 inline void init(int x,int y,int v){ 29 e[++tot].y=y;e[tot].v=v;e[tot].nex=head[x];head[x]=tot; 30 } 31 inline void init1(int x,int y){ 32 if(x==y)return; 33 e1[++tot1].y=y;e1[tot1].nex=head1[x];head1[x]=tot1; 34 } 35 void dfs(int x,int pa){ 36 fa[x][0]=pa;id[x]=++cnt; 37 for(int i=1;fa[fa[x][i-1]][i-1]!=0;++i)fa[x][i]=fa[fa[x][i-1]][i-1]; 38 for(int i=head[x];i;i=e[i].nex){ 39 int y=e[i].y; 40 if(y==pa)continue; 41 dis[y]=dis[x]<e[i].v?dis[x]:e[i].v; 42 dep[y]=dep[x]+1; 43 dfs(y,x); 44 } 45 } 46 bool mcmp(int x,int y){return id[x]<id[y];} 47 int getlca(int x,int y){ 48 if(dep[x]<dep[y])swap(x,y); 49 for(int i=19;i>=0;--i){ 50 if(!fa[x][i])continue; 51 if(dep[fa[x][i]]>=dep[y])x=fa[x][i]; 52 if(dep[x]==dep[y])break; 53 } 54 if(x==y)return x; 55 for(int i=19;i>=0;--i){ 56 if(!fa[x][i])continue; 57 if(fa[x][i]!=fa[y][i]){x=fa[x][i];y=fa[y][i];} 58 } 59 return fa[x][0]; 60 } 61 void dfs1(int x){ 62 f[x]=dis[x];LL v=0; 63 for(int i=head1[x];i;i=e1[i].nex){ 64 dfs1(e1[i].y); 65 v+=f[e1[i].y]; 66 } 67 if(v)f[x]=min(f[x],v); 68 head1[x]=0; 69 } 70 void cutit(){ 71 int q=mread(); for(int i=1;i<=q;++i)a[i]=mread(); 72 sort(a+1,a+1+q,mcmp); tly=1; 73 for(int i=2;i<=q;++i){if(getlca(a[i],a[tly])!=a[tly])a[++tly]=a[i];} 74 //由根节点到叶子节点的每条链上至多只有一个点 75 tai=1;sta[1]=1;tot1=0;int lc; 76 for(int i=1;i<=tly;i++){//sta按照dfs序维护了一条链 77 lc=getlca(sta[tai],a[i]); 78 for(;tai>1;){//更改链向下延伸的方向,把之前方向的点连上 79 if(dep[sta[tai-1]]<=dep[lc]){ 80 init1(lc,sta[tai]);tai--; 81 if(sta[tai]!=lc)sta[++tai]=lc; 82 break; 83 }init1(sta[tai-1],sta[tai]);tai--; 84 } 85 if(sta[tai]!=a[i])sta[++tai]=a[i]; 86 } 87 while(--tai)init1(sta[tai],sta[tai+1]); 88 dfs1(1); 89 printf("%lld\n",f[1]); 90 } 91 int main(){ 92 int x,y,v; 93 n=mread(); 94 for(int i=1;i<n;++i){ 95 x=mread();y=mread();v=mread(); 96 init(x,y,v);init(y,x,v); 97 } 98 dis[1]=minf; dep[1]=0; dfs(1,0); 99 m=mread(); for(int i=1;i<=m;++i) cutit(); 100 return 0; 101 }