bzoj3242: [Noi2013]快餐店 树形dp+线段树

对于不是环上的点我们可以用树形dp解决,剩下的就是在一个环上找距离最远的两点。我们可以拆环,几下每个点第一大和第二大的距离

这样最长链就是max{sum[j]-sum[i]+dis[i]+dis[j]}。

于是用两颗线段树分别维护max{sum[j]+dis[j]}和max{dis[i]-sum[i]}。然后更新答案。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define ss printf("fuck\n");
#define oo 200000000000000LL;
#define sint long long
#define maxn 300100
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define getmid int mid=(l+r)>>1
int read()
{
    int res;char c;
    while(c=getchar(),c<'0'||c>'9');
    res=c-'0';
    while(c=getchar(),c>='0'&&c<='9')
        res=res*10+c-'0';
    return res;
}
sint sum[maxn],f[maxn],dis[maxn];
sint max1[maxn*4],max2[maxn*4],val[maxn];
sint max21[maxn*4],max22[maxn*4];
int n,label1[maxn*4],label2[maxn*4];
int label21[maxn*4],label22[maxn*4];
int next[maxn],first[maxn],to[maxn],en;
sint ans=-oo;
void print1(int x,int l,int r)
{
    printf("%d %d %d %d %d %lld %lld\n",x,l,r,label1[x],label2[x],max1[x],max2[x]);
}
void print2(int x,int l,int r)
{
    printf("%d %d %d %d %d %lld %lld\n",x,l,r,label21[x],label22[x],max21[x],max22[x]);
}
void add(int a,int b,sint c)
{
    en++;
    to[en]=b;val[en]=c;
    next[en]=first[a];
    first[a]=en;
}
int cir1[maxn],tot,ed;
bool vis[maxn];
bool dfs(int now,int last)
{
    //printf("%d %d\n",now,last);
    //system("pause");
    vis[now]=1;
    for(int i=first[now];i;i=next[i])
    {
        int v=to[i];
        if(v==last) continue;
        if(vis[v])
        {
            tot++;ed=v;
            cir1[tot]=now;
            return true;
        }
        if(dfs(v,now))
        {
            tot++;
            cir1[tot]=now;
            if(now!=ed)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    vis[now]=false;
    return false;
}
void dfs2(int x,int last)
{
    sint maxl=0;
    for(int i=first[x];i;i=next[i])
    {
        int v=to[i];
        if(vis[v]||v==last) continue;
        dfs2(v,x);
        if(f[v]+val[i]>=f[x])
        {
            maxl=f[x];
            f[x]=f[v]+val[i];
        }
        else
        {
            maxl=max(maxl,f[v]+val[i]);
        }
    }
    ans=max(ans,f[x]+maxl);
}
void pushup1(int rt)
{
    if(max1[rt<<1]>max1[rt<<1|1])
    {
        label1[rt]=label1[rt<<1];
        max1[rt]=max1[rt<<1];
        if(max2[rt<<1]>max1[rt<<1|1])
        {
            label2[rt]=label2[rt<<1];
            max2[rt]=max2[rt<<1];
        }
        else
        {
            label2[rt]=label1[rt<<1|1];
            max2[rt]=max1[rt<<1|1];
        }
    }
    else if(max1[rt<<1]==max1[rt<<1|1])
    {
        label1[rt]=label1[rt<<1];
        label2[rt]=label1[rt<<1|1];
        max2[rt]=max1[rt]=max1[rt<<1];
    }
    else
    {
        label1[rt]=label1[rt<<1|1];
        max1[rt]=max1[rt<<1|1];
        if(max2[rt<<1|1]>max1[rt<<1])
        {
            label2[rt]=label2[rt<<1|1];
            max2[rt]=max2[rt<<1|1];
        }
        else
        {
            label2[rt]=label1[rt<<1];
            max2[rt]=max1[rt<<1];
        }
    }
}
void build1(int l,int r,int rt)
{
    if(l==r)
    {
        max1[rt]=dis[l]+sum[l];
        max2[rt]=-oo;
        label1[rt]=cir1[l];
        //print1(rt,l,r);
        return;
    }
    getmid;
    build1(lson);
    build1(rson);
    pushup1(rt);
    //print1(rt,l,r);
}
void pushup2(int rt)
{
    if(max21[rt<<1]>max21[rt<<1|1])
    {
        label21[rt]=label21[rt<<1];
        max21[rt]=max21[rt<<1];
        if(max22[rt<<1]>max21[rt<<1|1])
        {
            label22[rt]=label22[rt<<1];
            max22[rt]=max22[rt<<1];
        }
        else
        {
            label22[rt]=label21[rt<<1|1];
            max22[rt]=max21[rt<<1|1];
        }
    }
    else if(max21[rt<<1]==max21[rt<<1|1])
    {
        label21[rt]=label21[rt<<1];
        label22[rt]=label21[rt<<1|1];
        max22[rt]=max21[rt]=max21[rt<<1];
    }
    else
    {
        label21[rt]=label21[rt<<1|1];
        max21[rt]=max21[rt<<1|1];
        if(max22[rt<<1|1]>max21[rt<<1])
        {
            label22[rt]=label22[rt<<1|1];
            max22[rt]=max22[rt<<1|1];
        }
        else
        {
            label22[rt]=label21[rt<<1];
            max22[rt]=max21[rt<<1];
        }
    }
}
void build2(int l,int r,int rt)
{
    if(l==r)
    {
        max21[rt]=dis[l]-sum[l];
        max22[rt]=-oo;
        label21[rt]=cir1[l];
        return;
    }
    getmid;
    build2(lson);
    build2(rson);
    pushup2(rt);
    //print2(rt,l,r);
}
struct node
{
    int num;
    sint val;
    void cl()
    {
        num=0;val=-oo;
    }
};
bool operator < (node aa,node bb)
{
    if(aa.val!=bb.val) return aa.val<bb.val;
    return aa.num<bb.num;
}
node query1(int l,int r,int rt,int x,int y)
{
    if(x<=l&&r<=y)
    {
        node tmp;
        tmp.val=max1[rt];
        tmp.num=label1[rt];
        return tmp;
    }
    getmid;
    node t1,t2;
    t1.cl();t2.cl();
    if(x<=mid) t1=query1(lson,x,y);
    if(y>mid) t2=query1(rson,x,y);
    if(t1<t2) swap(t1,t2);
    return t1;
}
node query2(int l,int r,int rt,int x,int y)
{
    if(x<=l&&r<=y)
    {
        node tmp;
        tmp.val=max2[rt];
        tmp.num=label2[rt];
        return tmp;
    }
    getmid;
    node t1,t2;
    t1.cl();t2.cl();
    if(x<=mid) t1=query2(lson,x,y);
    if(y>mid) t2=query2(rson,x,y);
    if(t1.val<t2.val) swap(t1,t2);
    return t1;
}
node query21(int l,int r,int rt,int x,int y)
{
    if(x<=l&&r<=y)
    {
        node tmp;
        tmp.val=max21[rt];
        tmp.num=label21[rt];
        return tmp;
    }
    getmid;
    node t1,t2;
    t1.cl();t2.cl();
    if(x<=mid) t1=query21(lson,x,y);
    if(y>mid) t2=query21(rson,x,y);
    if(t1<t2) swap(t1,t2);
    return t1;
}
node query22(int l,int r,int rt,int x,int y)
{
    if(x<=l&&r<=y)
    {
        node tmp;
        tmp.val=max22[rt];
        tmp.num=label22[rt];
        return tmp;
    }
    getmid;
    node t1,t2;
    t1.cl();t2.cl();
    if(x<=mid) t1=query22(lson,x,y);
    if(y>mid) t2=query22(rson,x,y);
    if(t1<t2) swap(t1,t2);
    return t1;
}
sint solve(int x)
{
    node tmp1=query1(1,tot*2,1,x,x+tot-1);
    node tmp2=query2(1,tot*2,1,x,x+tot-1);
    node tmp21=query21(1,tot*2,1,x,x+tot-1);
    node tmp22=query22(1,tot*2,1,x,x+tot-1);
    sint v=-oo;
    if(tmp1.num!=tmp21.num)
        v=max(v,tmp1.val+tmp21.val);
    if(tmp1.num!=tmp22.num)
        v=max(v,tmp1.val+tmp22.val);
    if(tmp2.num!=tmp21.num)
        v=max(v,tmp2.val+tmp21.val);
    if(tmp2.num!=tmp22.num)
        v=max(v,tmp2.val+tmp22.val);
    return v;
}
int main()
{
  
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int a,b;
        sint c;
        a=read();b=read();c=read();
 
        add(a,b,c);
        add(b,a,c);
    }
    //printf("orz1");
    dfs(1,0);
    //printf("orz1");
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=tot;i++)
    {
        vis[cir1[i]]=1;
    }
    for(int i=1;i<=tot;i++)
    {
        dfs2(cir1[i],0);
    }
    for(int i=1;i<=tot;i++)
    {
        dis[i]=f[cir1[i]];
    }
    for(int i=1;i<=tot;i++)
    {
        cir1[i+tot]=cir1[i];
        dis[i+tot]=dis[i];
    }
    for(int i=2;i<=tot*2;i++)
    {
        for(int j=first[cir1[i]];j;j=next[j])
        {
            if(to[j]==cir1[i-1])
            {
                sum[i]=sum[i-1]+val[j];
                break;
            }
        }
    }
    build1(1,tot*2,1);
    build2(1,tot*2,1);
 
    sint ans2=oo;
    for(int i=1;i<=tot+1;i++)
    {
        ans2=min(ans2,solve(i));
    }
    ans=max(ans,ans2);
    printf("%.1lf",double(ans)/2);
 
    return 0;
}
/*
4
1 2 1
1 4 2
1 3 2
2 4 1
*/


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值