【JZOJ5336】【NOIP2017提高模拟】提米树(DP、前缀和)

来源:互联网 发布:js获取鼠标当前位置 编辑:程序博客网 时间:2024/06/05 12:47

Description

这里写图片描述

Solution

首先剪枝是对于一个点的,就是要把这个点下面的所有边给删掉。
然后有些点是两两不能做相邻的叶子的,只有dfs序相邻的叶子到他们lca上的点之间可以做相邻的叶子对,这样可以做到dfs序相邻。
然后我们可以设f[i]表示以i节点作为dfs序结尾的最大决心数量,那么枚举相邻的叶子,然后把上面的点两两配对来更新,这样是n^2log(带lca)的。但是要注意当一个点被更新多次的时候不能去max,比如1,2叶子对叶子上的x更新了一遍,然后当2,3叶子匹配的时候,有对x进行更新操作此时的x是只能属于dfs序最大的那个叶子节点的,所以只能赋值。
然后可以用前缀max来优化可以优化到O(n)。
相邻的两两叶子求lca,可以直接暴力的求,这样是O(n)的。

Code

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)#define rep(i,a) for(i=first[a];i;i=next[i])using namespace std;const int maxn=1e5+7,mo=1e9+7;int i,j,k,l,t,n,m,ans;int first[maxn*2],next[maxn*2],last[maxn*2],num,a[maxn],dfn[maxn],dfx;int fa[maxn],deep[maxn],size[maxn],g[maxn],p[maxn],x,y,o,f[maxn],w[maxn],b[maxn];int u[maxn],v[maxn],yi,er;void add(int x,int y){    last[++num]=y,next[num]=first[x],first[x]=num;}void dfs(int x,int y){    int i,j=0;deep[x]=deep[y]+1,fa[x]=y;dfn[++dfx]=x;w[x]=dfx;    rep(i,x){        if(last[i]!=y){            j=1;            dfs(last[i],x);        }    }    if(!j)b[++b[0]]=x;}int main(){//  freopen("fan.in","r",stdin);    scanf("%d",&n);    fo(i,1,n){        scanf("%d",&a[i]);        scanf("%d",&k);        fo(j,1,k)scanf("%d",&l),add(i,l),add(l,i);    }    dfs(1,0);    x=b[1];while(x)f[x]=a[x],x=fa[x];    fo(i,2,b[0]){        u[0]=v[0]=0;        x=b[i-1],y=b[i];        while(deep[x]>deep[y])u[++u[0]]=x,x=fa[x];        while(deep[y]>deep[x])v[++v[0]]=y,y=fa[y];        while(x!=y)u[++u[0]]=x,v[++v[0]]=y,y=fa[y],x=fa[x];        memset(p,0,sizeof(p));yi=er=g[0]=-mo;        fod(j,u[0],1)p[j]=max(p[j+1],a[fa[u[j]]]);        fo(j,1,u[0])g[j]=max(g[j-1],f[u[j]]-p[j]);        k=u[0];        fod(j,v[0],1){            yi=max(yi,a[fa[v[j]]]);            while(k&&yi>p[k])er=max(er,f[u[k]]),k--;            f[v[j]]=max(er-yi+a[v[j]],g[k]+a[v[j]]);        }    }    x=dfn[n];while(x)ans=max(f[x],ans),x=fa[x];    printf("%d\n",ans);}
原创粉丝点击