您的位置:首页 > 其它

BZOJ 3611 [Heoi2014]:虚树+树形DP

2016-10-05 01:02 211 查看
时空隧道

今天考试T3正解是虚树…..(看到这个名字我好虚啊….)

现在我们需要处理一棵树上k个点的询问,做一遍树形DP……

复杂度是O(n)的,q个询问,感觉复杂度很爆炸…..>_<……

然后我们可以发现对于每次询问k都不是很大,有用的点貌似不超过2*k个,那么其他点都是来打酱油的?(你来打酱油还徒增复杂度??扔出去斩了QAQ)….

那么问题来了…..肿么拎出来建树??求出所有点的LCA???这就到k^2了,说不定是退化了……再想想?其实并不需要k^2个LCA,想一想就会发现,其实只有k-1个LCA(可以想一想k=3时的情况,你绝对找不出来3个LCA)……那么问题就好办了,复杂度可以降到O(2*k)了……..至于怎么建树?单调栈出场….

我们按照dfs序扫过去,维护一个dep单调不下降的栈,如果当前元素dep小于栈顶,那么就不断把栈顶与前一个元素合并(就是求LCA)……

接下来说DP:

对于求和:

f[i]代表以i为根的子树中询问点的路径总长度,siz[i]代表子树中询问点的个数

转移很简单f[root]+=f[to[i]]+siz[to[i]]*(k-siz[to[i]])*dis[root,to[i]]

(画个图YY一下就懂了)….

对于最大值:

MAX[i]代表子树中最长链(询问点到根)的长度

MAX[root]=max(MAX[root],MAX[to[i]]+dis[root,to[i]])

对于最小值:

MIN[i]代表子树中最短链(询问点到根)的长度

MIN[root]=min(MIN[root],MIN[to[i]]+dis[root,to[i]])

**然而感觉真的建出来有些浪费,在我们维护单调栈的过程中就相当于进行了一次dfs,直接DP就好….然而我只写了暴力重新建树的做法QAQing…..

代码如下:

这个代码很慢,并且有一个数据过不去(QAQ)……今天太晚了,苯宝宝想去睡觉了,明天再改……

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;
const int maxn=1000000+5;
int stk[maxn],tail,siz[maxn],M[maxn],is[maxn],k;
int n,q,hd[maxn],to[maxn*2],nxt[maxn*2],cnt,dis[maxn],fa[maxn][20+5],G[maxn];
long long sum[maxn],MAX[maxn],MIN[maxn],minans,maxans;
inline void add(int x,int y){
to[cnt]=y;
nxt[cnt]=hd[x];
hd[x]=cnt++;
}
inline void dfs(int root,int f){
M[root]=++cnt;
for(int i=hd[root];i!=-1;i=nxt[i])
if(to[i]!=f)
fa[to[i]][0]=root,dis[to[i]]=dis[root]+1,dfs(to[i],root);
}
inline void init(void){
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
inline int LCA(int x,int y){
if(dis[x]>dis[y])
swap(x,y);
int d=dis[y]-dis[x];
for(int i=20;i>=0;i--)
if((1<<i)&d)
y=fa[y][i];
if(x==y)
return x;
for(int j=20;j>=0;j--)
if(fa[x][j]!=fa[y][j])
x=fa[x][j],y=fa[y][j];
return fa[x][0];
}
inline void dfs2(int root,int f){
siz[root]=is[root],sum[root]=0,MIN[root]=0x3f3f3f3f,MAX[root]=0;
for(int i=hd[root];i!=-1;i=nxt[i])
if(to[i]!=f){
dfs2(to[i],root);
siz[root]+=siz[to[i]];
minans=min(minans,MIN[root]+MIN[to[i]]+dis[to[i]]-dis[root]);
maxans=max(maxans,MAX[root]+MAX[to[i]]+dis[to[i]]-dis[root]);
sum[root]+=sum[to[i]]+siz[to[i]]*(k-siz[to[i]])*(dis[to[i]]-dis[root]);
MIN[root]=min(MIN[root],MIN[to[i]]+dis[to[i]]-dis[root]);
MAX[root]=max(MAX[root],MAX[to[i]]+dis[to[i]]-dis[root]);
}
if(is[root])
minans=min(minans,MIN[root]),maxans=max(maxans,MAX[root]),MIN[root]=0;
}
inline bool cmp(int x,int y){
return M[x]<M[y];
}
signed main(void){
freopen("project.in","r",stdin);
freopen("project.out","w",stdout);
cnt=0;memset(hd,-1,sizeof(hd));scanf("%d",&n);
for(int i=1,x,y;i<n;i++)
scanf("%d%d",&x,&y),add(x,y),add(y,x);
dis[1]=0,fa[1][0]=1,cnt=0;dfs(1,-1);init();
scanf("%d",&q);
while(q--){
scanf("%d",&k);
memset(is,0,sizeof(is));
for(int i=1;i<=k;i++)
scanf("%d",&G[i]),is[G[i]]=1;
sort(G+1,G+k+1,cmp);
tail=0;memset(hd,-1,sizeof(hd));cnt=0;
for(int i=1;i<=k;i++){
while(tail>1&&dis[LCA(stk[tail],stk[tail-1])]>=dis[LCA(G[i],stk[tail])]){
int a=stk[tail--],b=stk[tail--],c;
c=LCA(a,b),stk[++tail]=c;
if(a!=c)
add(a,c),add(c,a)/*,cout<<a<<" "<<c<<endl*/;
if(b!=c)
add(b,c),add(c,b)/*,cout<<b<<" "<<c<<endl*/;
}
stk[++tail]=G[i];
}
while(tail>1){
/*  int a=stk[tail--],b=stk[tail--],c;
c=LCA(a,b),stk[++tail]=c;
if(a!=c)
add(a,c),add(c,a);
if(b!=c)
add(b,c),add(c,b);
*/
add(stk[tail],stk[tail-1]),add(stk[tail-1],stk[tail]),tail--;
}
minans=0x3f3f3f3f,maxans=0;dfs2(stk[1],-1);
printf("%lld %lld %lld\n",sum[stk[1]],minans,maxans);
}
return 0;
}


by >_< NeighThorn
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: