[NOI2015]小园丁与老司机

190 篇文章 2 订阅
84 篇文章 2 订阅

题面

题意

给出n棵树,从原点开始,每次可以在左、右、上、左上 45∘、右上 45∘五个方向中选择一个,然后一直向这个方向走,直至走到一棵未经过的树,然后继续选择方向,直至5个方向都不存在未经过的树。
现在要经过尽可能多的树,输出最多可能经过的树的数量以及任意一条路径。
现在定义向上、左上 45∘、右上 45∘三个方向走形成的线段不优美,现在要覆盖所有最优路径中的不优美线段,每次从原点出发,行走方式与之前相同,问至少几次可以覆盖这些线段。

做法

这个问题可以拆成两个子问题。
1.求最多经过的树的数量并输出任意一条路径:
首先预处理出每棵树和原点向上、左上 45∘、右上 45∘三个方向所能到达的树,然后将y坐标离散化,每一层根据x坐标排序,然后就可以dp求解:
记dp[i]表示从i点进入这层所能到的树的数量的最大值,自上而下逐层状态转移,转移时可以发现对于从u进入该层,v出该层的转态,可以经过该层中的树的数量为:
1.u==v 只有1棵
2.u < v时 v和v左边的所有树。
3.u > v时 v和v右边的所有树。
然后只要对每一层扫两遍即可dp,dp时顺便记录一下路径即可。
2.求至少几次覆盖所有最优路径中的不优美线段:
首先因为数据的约束,不优美的线段数量不可能太多,因此根据每个点的dp值,dfs出所有最优路径(注意去重),然后问题就可以转化为有上下界的最小流(每条边的上限为INF,下限为1),因为图较特殊,所以有较简单的做法:
首先统计出每个点的入度(下界的入度和)与出度(下界的出度和)之差,若为正,则由超级源点向它连流量为差值的边,并将其计入答案,若为负则由它向超级汇点连流量为该差值的绝对值的边,此时的答案减去此图的最大流即为需要的覆盖次数。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include<cstring>
#define INF 0x3f3f3f3f
#define P pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define N 50010
using namespace std;

int n,cy,ty[N],dp[N],lj[N],mx[N],ml[N],pos[N],first[N],ds[N],deep[N],cur[N],bb=1,S,T,ans;
bool vis[N];
struct Node
{
    int x,y,id;
}node[N],tp[N];
struct Bn
{
    int to,next,quan;
}bn[1001000];
vector<int>iy[N],to[N],can[N],st;
map<int,int>zhy;
map<P,bool>mm;
queue<int>que;

inline bool cmp(const int &u,const int &v){return node[u].x<node[v].x;}
inline bool cmp1(const Node &u,const Node &v){return u.x<v.x || u.x==v.x&&u.y<v.y;}
inline bool cmp2(const Node &u,const Node &v){int t=(u.x-u.y)-(v.x-v.y);return t<0||!t&&u.y<v.y;}
inline bool cmp3(const Node &u,const Node &v){int t=(u.x+u.y)-(v.x+v.y);return t<0||!t&&u.y<v.y;}

inline void add(int u,int v,int w)
{
    bb++;
    bn[bb].to=v;
    bn[bb].quan=w;
    bn[bb].next=first[u];
    first[u]=bb;
}

inline void ad(int u,int v,int w)
{
    if(mm[mp(u,v)]) return;
    mm[mp(u,v)]=mm[mp(v,u)]=1;
    ds[v]++,ds[u]--;
    add(u,v,w);
    add(v,u,0);
}

inline bool bfs()
{
    int i,j,p,q;
    memset(deep,0,sizeof(deep));
    deep[S]=1;
    que.push(S);
    for(;!que.empty();)
    {
        q=que.front();
        que.pop();
        for(p=first[q];p!=-1;p=bn[p].next)
        {
            if(deep[bn[p].to] || !bn[p].quan) continue;
            deep[bn[p].to]=deep[q]+1;
            que.push(bn[p].to);
        }
    }
    return deep[T];
}

int dfs(int now,int mn)
{
    if(now==T) return mn;
    int res;
    for(int &p=cur[now];p!=-1;p=bn[p].next)
    {
        if(!bn[p].quan || deep[bn[p].to]!=deep[now]+1) continue;
        res=dfs(bn[p].to,min(mn,bn[p].quan));
        if(res)
        {
            bn[p].quan-=res;
            bn[p^1].quan+=res;
            return res;
        }
    }
    return 0;
}

inline void out(int w,int u,int v)
{
    int i,j;
    if(v==-1)
    {
        for(i=u;i>=0;i--) printf("%d ",iy[w][i]);
        for(i=u+1;i<iy[w].size();i++) printf("%d ",iy[w][i]);
        return;
    }
    if(u==v)
    {
        printf("%d ",iy[w][u]);
        return;
    }
    if(u<v)
    {
        for(i=u;i>=0;i--) printf("%d ",iy[w][i]);
        for(i=u+1;i<=v;i++) printf("%d ",iy[w][i]);
    }
    else
    {
        for(i=u;i<iy[w].size();i++) printf("%d ",iy[w][i]);
        for(i=u-1;i>=v;i--) printf("%d ",iy[w][i]);
    }
}

inline int len(int w,int u,int v)
{
    if(v==-1) return iy[w].size();
    if(u==v) return 1;
    if(u<v) return v+1;
    return iy[w].size()-v;
}

void Dfs(int now)
{
    if(vis[now]) return;
    vis[now]=1;
    int i,j,t=zhy[node[now].y],p;
    for(i=0;i<iy[t].size();i++)
    {
        p=iy[t][i];
        if(!can[p].size()) continue;
        if(dp[now]-dp[can[p][0]]==len(t,pos[now],i))
        {
            for(j=0;j<can[p].size();j++)
            {
                ad(p,can[p][j],INF);
                Dfs(can[p][j]);
            }
        }
    }
}

int main()
{
    memset(first,-1,sizeof(first));
    int i,j,k,t,tmp,tl;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&node[i].x,&node[i].y);
        node[i].id=i;
        ty[i]=node[i].y;
        tp[i]=node[i];
    }
    sort(ty+1,ty+n+1);
    for(i=1;i<=n;i++) if(ty[i]!=ty[i-1] || i==1) zhy[ty[i]]=++cy;
    for(i=1;i<=n;i++) iy[zhy[node[i].y]].push_back(i);
    for(i=1;i<=cy;i++) sort(iy[i].begin(),iy[i].end(),cmp);

    sort(tp+1,tp+n+1,cmp1);for(i=1;i<=n;i++) if(tp[i].x==tp[i-1].x && i!=1) to[tp[i-1].id].push_back(tp[i].id);else if(!tp[i].x) st.push_back(tp[i].id);
    sort(tp+1,tp+n+1,cmp2);for(i=1;i<=n;i++) if(tp[i].x-tp[i].y==tp[i-1].x-tp[i-1].y && i!=1) to[tp[i-1].id].push_back(tp[i].id);else if(tp[i].x==tp[i].y) st.push_back(tp[i].id);
    sort(tp+1,tp+n+1,cmp3);for(i=1;i<=n;i++) if(tp[i].x+tp[i].y==tp[i-1].x+tp[i-1].y && i!=1) to[tp[i-1].id].push_back(tp[i].id);else if(tp[i].x==-tp[i].y) st.push_back(tp[i].id);
    if(!st.size())
    {
        puts("0");
        puts("");
        puts("0");
        return 0;
    }

    for(i=cy;i>=1;i--)
    {
        for(j=0;j<iy[i].size();j++)
        {
            t=iy[i][j];
            pos[t]=j;
            dp[t]=iy[i].size();
            lj[t]=-1;
            for(k=0;k<to[t].size();k++)
            {
                if(dp[to[t][k]]>mx[t])
                {
                    can[t].clear();
                    can[t].push_back(to[t][k]);
                    mx[t]=dp[to[t][k]];
                    ml[t]=to[t][k];
                }
                else if(dp[to[t][k]]==mx[t])
                {
                    can[t].push_back(to[t][k]);
                }
            }
            if(mx[t]+1>dp[t])
            {
                dp[t]=mx[t]+1;
                lj[t]=t;
            }
        }
        tmp=0;
        for(j=iy[i].size()-2;j>=0;j--)
        {
            t=iy[i][j+1];
            if(mx[t]+j+2>tmp)
            {
                tmp=mx[t]+j+2;
                tl=t;
            }
            t=iy[i][j];
            if(tmp>dp[t])
            {
                dp[t]=tmp;
                lj[t]=tl;
            }
        }
        tmp=0;
        for(j=1;j<iy[i].size();j++)
        {
            t=iy[i][j-1];
            if(mx[t]+iy[i].size()-j+1>tmp)
            {
                tmp=mx[t]+iy[i].size()-j+1;
                tl=t;
            }
            t=iy[i][j];
            if(tmp>dp[t])
            {
                dp[t]=tmp;
                lj[t]=tl;
            }
        }
    }
    tmp=0;
    for(i=0;i<st.size();i++)
    {
        if(dp[st[i]]>tmp)
        {
            can[0].clear();
            can[0].push_back(st[i]);
            tmp=dp[st[i]];
            tl=st[i];
        }
        else if(dp[st[i]]==tmp)
        {
            can[0].push_back(st[i]);
        }
    }

    cout<<tmp<<endl;
    for(;;)
    {
        t=zhy[node[tl].y];
        if(lj[tl]!=-1)
        {
            out(t,pos[tl],pos[lj[tl]]);
            tl=ml[lj[tl]];
        }
        else
        {
            out(t,pos[tl],lj[tl]);
            break;
        }
    }
    puts("");

    for(i=0;i<can[0].size();i++) Dfs(can[0][i]),ad(0,can[0][i],INF);
    S=n+1,T=n+2;
    for(i=0;i<=n;i++)
    {
        if(ds[i]>0)
        {
            ans+=ds[i];
            ad(S,i,ds[i]);
        }
        else if(ds[i]<0) 
        {
            ad(i,T,-ds[i]);
        }
    }
    for(;bfs();)
    {
        memcpy(cur,first,sizeof(cur));
        for(t=dfs(S,INF);t;ans-=t,t=dfs(S,INF));
    }
    cout<<ans;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值