[BZOJ3572][HNOI2014]世界树

2 篇文章 0 订阅

这一题调试了蛮久,而且我一写虚树常数就大,是rank1的17倍。
显然是一道虚树题。建出虚树后,先两遍DFS处理出每个虚树上的点的归属,然后考虑虚树每一条边的贡献。
定义 size 为原树每个子树大小
对于一条边 (u,v) ,设 x 为原树中u的儿子 v 的祖先。
bel[u]=bel[v] ans[bel[u]]+=size[x]size[v]
否则,在原树 uv 链上二分一个点 mid ,使 mid 下面的点属于 bel[v] ,上面的属于 bel[u] ans[bel[u]]+=size[x]size[mid],ans[bel[v]]+=size[mid]size[v] ,最后还要处理不在虚树上的子树,这些子树一定隶属它在虚树上的根的管辖,记一个 rem 数组维护这些子树大小即可。
妈的调了3个小时的jg题。我不做工业题谁做工业题。

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<sstream>
#include<climits>
#define X first
#define Y second
#define DB double
#define lc now<<1
#define rc now<<1|1
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
template<typename T>void Read(T& x)
{
    x=0;int flag=0,sgn=1;char c;
    while(c=getchar())
    {
        if(c=='-')sgn=-1;
        else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
        else if(flag)break;
    }
    x*=sgn;
}
const int MAXN=601000;
int n,m,k,h[MAXN],ans[MAXN],pa[MAXN],iskey[MAXN];
int A[MAXN],size_a=0;
int dep[MAXN],fa[MAXN][20],bel[MAXN],dfn[MAXN],size[MAXN],rem[MAXN];
pii clo[MAXN];
struct Tree{
    int e,dfs_clock,first[MAXN],next[MAXN],to[MAXN];
    void init()
    {
        memset(first,0,sizeof(first));
        e=dfs_clock=0;
    }
    void add(int u,int v)
    {//if you judge i==0 instead of i==-1 ++e first
        ++e;next[e]=first[u];first[u]=e;to[e]=v;
        ++e;next[e]=first[v];first[v]=e;to[e]=u;
    }
    void dfs(int u,int f)
    {
        if(u==1)dep[u]=1;
        else dep[u]=dep[f]+1;
        fa[u][0]=f;
        size[u]=1;
        dfn[u]=++dfs_clock;
        for(int i=1;i<20;i++)
            fa[u][i]=fa[fa[u][i-1]][i-1];
        for(int i=first[u];i;i=next[i])
        {
            int v=to[i];
            if(v==f)continue;
            dfs(v,u);
            size[u]+=size[v];
        }
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])swap(x,y);
        if(!y)return y;
        for(int i=18;i>=0;i--)
            if(dep[fa[x][i]]>=dep[y])
                x=fa[x][i];
        if(x==y)return x;
        for(int i=18;i>=0;i--)
            if(fa[x][i]&&fa[y][i]&&fa[x][i]!=fa[y][i])
                x=fa[x][i],
                y=fa[y][i];
        return fa[x][0];
    }
}ori,vit;
int dis(int u,int v)
{
    return dep[u]+dep[v]-2*dep[ori.LCA(u,v)];
}
void dfs1(int u,int f)
{
    rem[u]=size[u];
    for(int i=vit.first[u];i;i=vit.next[i])
    {
        int v=vit.to[i];
        if(v==f)continue;
        if(u!=0)
        {
            int D=dis(u,v);
            if(clo[v].X>clo[u].X+D)//> instead of <
                clo[v].Y=clo[u].Y,
                clo[v].X=clo[u].X+D;
            else if(clo[v].X==clo[u].X+D)//NO judging
                if(clo[u].Y<clo[v].Y)
                    clo[v].Y=clo[u].Y;
        }
        dfs1(v,u);
    }
    bel[u]=clo[u].Y;
}
void dfs2(int u,int f)
{
    for(int i=vit.first[u];i;i=vit.next[i])
    {
        int v=vit.to[i];
        if(f==v)continue;
        dfs2(v,u);
        int D=dis(u,v);
        if(clo[u].X>clo[v].X+D)
        {
            clo[u].X=clo[v].X+D;
            clo[u].Y=clo[v].Y;
        }
        else if(clo[u].X==clo[v].X+D&&clo[v].Y<clo[u].Y)
        {
            clo[u].X=clo[v].X+D;
            clo[u].Y=clo[v].Y;
        }   
    }
}
void fuck(int u,int v)//algorithm error
{
    int mid=v,a=v;
    for(int i=18;i>=0;i--)
        if(fa[a][i]&&dep[fa[a][i]]>dep[u])
            a=fa[a][i];
    rem[u]-=size[a];
    if(bel[u]==bel[v])
    {
        ans[bel[u]]+=size[a]-size[v];
        return;
    }
    for(int i=18;i>=0;i--)
        if(fa[mid][i]&&dis(fa[mid][i],bel[u])>dis(fa[mid][i],bel[v]))
            mid=fa[mid][i];
    if(dis(fa[mid][0],bel[u])==dis(fa[mid][0],bel[v]))
        if(bel[u]>bel[v])
            mid=fa[mid][0];
    ans[bel[v]]+=size[mid]-size[v];
    ans[bel[u]]+=size[a]-size[mid];
}
void work(int u,int f)
{
    if(f==0)
        ans[bel[u]]+=n-size[u];
    for(int i=vit.first[u];i;i=vit.next[i])
    {
        int v=vit.to[i];
        if(v==f)continue;
        if(u!=0)
            fuck(u,v);
        work(v,u);
    }
    ans[bel[u]]+=rem[u];
}
int top,st[MAXN];
bool cmp(const int& x,const int& y)
{
    return dfn[x]<dfn[y];
}
void solve()
{
    size_a=0;
    vit.init();
    top=0;
    st[++top]=0;
    sort(h+1,h+k+1,cmp);
    for(int i=1;i<=k;i++)
    {
        int x=ori.LCA(st[top],h[i]);
        A[size_a++]=x;
        A[size_a++]=h[i];
        for(;dep[st[top]]>dep[x];--top)
            if(dep[st[top-1]]<=dep[x])
                pa[st[top]]=x;
        if(x!=st[top])
        {
            pa[x]=st[top];
            st[++top]=x;
        }
        pa[st[++top]=h[i]]=x;
    }
    sort(A,A+size_a);
    size_a=unique(A,A+size_a)-A;
    for(int i=0;i<size_a;i++)
        vit.first[A[i]]=0;
    for(int i=0;i<size_a;i++)
    {
        if(A[i]==0)continue;
        //DEBUG("%d %d\n",A[i],pa[A[i]]);
        vit.add(A[i],pa[A[i]]);
    }
    clo[0].X=INF;
    for(int i=0;i<size_a;i++)
        if(iskey[A[i]])
            clo[A[i]].X=0,clo[A[i]].Y=A[i];
        else
            clo[A[i]].X=INF;
    dfs2(0,0);//dfs2 first!
    dfs1(0,0);  
    work(0,-1);
}
int tmp[MAXN];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
#endif
    ori.init();
    Read(n);
    for(int i=1;i<n;i++)
    {
        int a,b;
        Read(a);Read(b);
        ori.add(a,b);
    }
    ori.dfs(1,0);
    Read(m);
    for(int i=1;i<=m;i++)
    {
        Read(k);
        for(int j=1;j<=k;j++)
            Read(h[j]),iskey[h[j]]=1;
        memcpy(tmp,h,sizeof(int)*(k+1));
        solve();
        for(int j=1;j<=k;j++)
        {
            printf("%d ",ans[tmp[j]]==0?1:ans[tmp[j]]);
            iskey[tmp[j]]=ans[tmp[j]]=0;
        }
        puts("");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值