码迷,mamicode.com
首页 > 数据库 > 详细

# UOJ Round48:Goodbye Wuxu C. 新年的小黄鸭

时间:2019-06-19 19:54:15      阅读:141      评论:0      收藏:0      [点我收藏+]

标签:++   转移   code   names   子节点   答案   space   ODB   n+1   

35 pts:

\(f[i][j]\)表示在\(i\)节点,当前重链顶端到当前点距离为\(j\)的最优方案。

复杂度是\(O(n^2)\)

100 pts:

自己的理解可能跟题解有点偏差。

考虑dp的一个\(f[i][j]\) 一定会转移到0之后才对非\(j-1\)的位置造成贡献。

我们转换一下,即每个位置所在的重链一定会通向叶子节点。

考虑我们能不能直接通过数据结构找出那个叶子节点。考虑这一条链,我们将这一条链每个点的权值设置为这个点的父亲的其他儿子到达这条链时的\(f\),选这条链得到的答案就是这条链上的权值和再加上走这条链的贡献了。

然后我们考虑这一条链上的值。我们在每个节点\(2^i+1\)级祖先打上这个点的标记,这样总共\(n\log\)个标记。

\(n\log\) 个标记可以直接区间加到叶子节点上,这题就可以两个log做出来了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int anc[N][17],n;
int hed[N],to[N<<1],nxt[N<<1],cnt,deg[N];
inline void adde(int u,int v){
    ++cnt;to[cnt]=v,nxt[cnt]=hed[u];hed[u]=cnt;deg[u]++;
}

int t[N<<2],lzy[N<<2];
inline void Add(int x,int l,int r,int ql,int qr,int sum){
    if(ql<=l&&qr>=r){t[x]+=sum,lzy[x]+=sum;return ;}
    int mid=(l+r)>>1;
    if(ql<=mid)Add(x<<1,l,mid,ql,qr,sum);
    if(qr>mid)Add(x<<1|1,mid+1,r,ql,qr,sum);
    t[x]=min(t[x<<1],t[x<<1|1])+lzy[x];
}
inline int qry(int x,int l,int r,int ql,int qr){
    if(ql<=l&&qr>=r)return t[x];
    int mid=(l+r)>>1;
    if(qr<=mid)return qry(x<<1,l,mid,ql,qr);
    if(ql>mid)return qry(x<<1|1,mid+1,r,ql,qr);
    return min(qry(x<<1,l,mid,ql,qr),qry(x<<1|1,mid+1,r,ql,qr))+lzy[x];
}

vector<int> Down[N];

int in[N],out[N],dfn;
int sz[N];

inline void dfs(int x,int pre){
    anc[x][0]=pre;
    in[x]=dfn+1;sz[x]=1;
    if(pre)Down[pre].push_back(x);
    for(int i=1;i<17;i++)anc[x][i]=anc[anc[x][i-1]][i-1];
    for(int i=0;i<17;i++)Down[anc[anc[x][i]][0]].push_back(x);
    if(deg[x]==1&&pre)++dfn;
    for(int i=hed[x];i;i=nxt[i])if(to[i]!=pre){
        int v=to[i];dfs(v,x);sz[x]+=sz[v];
    }
    out[x]=dfn;
}

int f[N];

inline void dfs2(int x,int pre){
    int sum=0;
    for(int i=hed[x];i;i=nxt[i]){int v=to[i];if(v==pre)continue;dfs2(v,x);}
    for(int i=hed[x];i;i=nxt[i]){
        int v=to[i];if(v==pre)continue;
        sum+=f[v]+sz[v];
    }
    for(int i=hed[x];i;i=nxt[i]){
        int v=to[i];if(v==pre)continue;
        Add(1,1,dfn,in[v],out[v],sum-f[v]-sz[v]);
    }
    for(size_t i=0;i<Down[x].size();i++){int v=Down[x][i];
        Add(1,1,dfn,in[v],out[v],sz[v]);
    }
    f[x]=qry(1,1,dfn,in[x],out[x]);
    for(size_t i=0;i<Down[x].size();i++){int v=Down[x][i];
        Add(1,1,dfn,in[v],out[v],-sz[v]);
    }
}

int main()
{
    cin >> n;
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        adde(u,v),adde(v,u);
    }
    dfs(1,0);dfs2(1,0);cout << f[1] << endl;
}

# UOJ Round48:Goodbye Wuxu C. 新年的小黄鸭

标签:++   转移   code   names   子节点   答案   space   ODB   n+1   

原文地址:https://www.cnblogs.com/weiyanpeng/p/11053816.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!