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 }
View Code

 

 

posted @ 2018-05-18 15:58  鲸头鹳  阅读(109)  评论(0编辑  收藏  举报