bzoj4200 [NOI2015]小园丁与老司机

(http://www.elijahqi.win/2017/12/22/bzoj4200-noi2015%E5%B0%8F%E5%9B%AD%E4%B8%81%E4%B8%8E%E8%80%81%E5%8F%B8%E6%9C%BA/)
题目描述

小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面。田野上有n棵 许愿树,编号 1,2,3, ⋯ , n ,每棵树可以看作平面上的一个点,其中第i棵树 (1 ≤ i ≤ n) 位于坐标 (xi, yi) 。任意两棵树的坐标均不相同。

老司机 Mr. P 从原点 (0,0) 驾车出发,进行若干轮行动。每一轮,Mr. P 首先选择任意一个满足以下条件的方向:

1.为左、右、上、左上 45°、右上 45°五个方向之一。

2.沿此方向前进可以到达一棵他尚未许愿过的树。

完成选择后,Mr. P 沿该方向直线前进,必须到达该方向上距离最近的尚未 许愿的树,在树下许愿并继续下一轮行动。如果没有满足条件的方向可供选择, 则停止行动。他会采取最优策略,在尽可能多的树下许愿。若最优策略不唯一, 可以选择任意一种。

不幸的是,小园丁 Mr. S 发现由于田野土质松软,老司机 Mr. P 的小汽车在 每轮行进过程中,都会在田野上留下一条车辙印,一条车辙印可看作以两棵树(或 原点和一棵树)为端点的一条线段。

在 Mr.P之后,还有很多许愿者计划驾车来田野许愿,这些许愿者都会像Mr. P 一样任选一种最优策略行动。Mr.S认为非左右方向(即上、左上 45°、右 上 45°三个方向)的车辙印很不美观,为了维护田野的形象,他打算租用一些轧路机,在这群许愿者到来之前夯实所有“可能留下非左右方向车辙印”的地面。“可能留下非左右方向车辙印”的地面应当是田野上的若干条线段,其中每条线 段都包含在某一种最优策略的行进路线中。每台轧路机都采取满足以下三个条件的工作模式:

1.从原点或任意一棵树出发。

2.只能向上、左上 45°、右上 45°三个方向之一移动,并且只能在树下改变方向或停止。

3.只能经过“可能留下非左右方向车辙印”的地面,但是同一块地面可以 被多台轧路机经过。

现在 Mr. P 和 Mr. S 分别向你提出了一个问题:

1.请给 Mr.P 指出任意一条最优路线。

2.请告诉 Mr.S 最少需要租用多少台轧路机。
输入输出格式

输入格式:

第 1 行包含 1 个正整数n ,表示许愿树的数量。

接下来 n 行,第 i + 1 行包含 2 个整数 xi , yi ,中间用单个空格隔开,表示第i棵许愿树的坐标。

输出格式:

包括 3 行。

第 1 行输出 1 个整数 m ,表示 Mr. P 最多能在多少棵树下许愿。

输出文件的第 2 行输出 m 个整数,相邻整数之间用单个空格隔开,表示 Mr.P 应该依次在哪些树下许愿。

输出文件的第 3 行输出 1 个整数,表示 Mr. S 最少需要租用多少台轧路机。
输入输出样例
输入样例#1: 复制

6
-1 1
1 1
-2 2
0 8
0 9
0 10

输出样例#1: 复制

3
2 1 3
3

输入样例#2: 复制

4
0 1
-2 1
2 1
3 2

输出样例#2: 复制

4
1 2 3 4
2

说明

【样例说明1】

最优路线2条可许愿3次:(0,0) → (1,1) → (−1,1) → (−2,2) 或 (0,0) → (0,8) → (0,9) → (0,10)。

至少 3 台轧路机,路线是 (0,0) → (1,1) , (−1,1) → (−2,2) 和 (0,0) → (0,8) → (0,9) → (0,10) 。

【样例说明2】

最优路线唯一:(0,0) → (0,1) → (−2,1) → (2,1) → (3,2) ,可许愿 4 次。其中在 (0,1) 许愿后,从(−2,1) 出发沿着向右的方向能够到达的最近的未许愿过的树是(2,1),所以可以到达 (2,1) 。

而如果沿着 (0,0) → (0,1) → (2,1) → (−2,1) 的方向前进,此时 (−2,1) 右边所有树都是许愿过的,根据题目条件规定,停止前进。故无法获得最优解。

(0,0) → (0,1) 与 (2,1) → (3,2) 会留下非左右方向车辙印,需 2 台轧路机。

这题 我读题加想题花了大概比zhx多一个多小时调试程序花的时间不知道比大佬多到多少 我好菜啊qwq题意是 我首先需要 算出我最多可以在多少个树下面许愿 我想了很久 其实确实可以一个dp解决 设dp[i]表示我到现在的第i号节点 我最多可以访问多少的许愿树 通过画这个样例 我可以清楚的看出这个是以纵坐标分层的图 然后可以看出来 我显然可以处理同一层的节点 然后再进行不同层之间的dp转移
那么这个同一层的dp转移怎么想 那显然就是我想要最大嘛 那就找一个 左边dp值+左边能许愿的树最大的这个值 或者 右边dp值+右边所有的许愿树的最大值
给我当前这个位置赋值 然后至于路径 我看原题n<=5000于是直接n^2转移路径 但是被uoj数据hack了 因为数据范围里有一条 可能存在同一层的点是50000个那么多 但是那样的话 我现在的方法就前功尽弃了 所以方法待填坑
我每次转移的时候记录我这个点是从哪里来的 然后不同层的点用from记录 同层的点用from1记录 然后注意后可能存在多个这样的值 于是我使用vector 存下来 然后打印路径的时候dfs一下 即可
最后第二问可能看一下 我觉得很像bzoj的滑雪那题 应该是一个无源汇上下界的最小流 那么我就直接在原来的基础上建图即可 注意就是 我有可能多个点的dp值相同 我需要分别从那几个点 开始dfs下去 根据题意只建from的边即可 然后跑一个最小流即可


//#pragma GCC optimize (2)
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 55000
#define inf 0x3f3f3f3f
#define inf1 0x7fffffff
using namespace std;
inline char gc(){
    static char now[1<<16],*T,*S;
    if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
struct node{
    int x,y,id;
}tr[N];
struct node1{
    int y,z,next;
}data[20*N];
inline bool cmp(node a,node b){return a.y==b.y?a.x<b.x:a.y<b.y;}
int line_from[N],line_to[N],n,dp[N],pre[5500],last[5500],dp1[N],bl[N],num=1,h[N],T,d[N],out[N],t,S,level[N],cur[N];bool flag[N];
vector<int> q_column[N],q1[N],q2[N],from[N],from1[N],tmp1(N),tmp2(N),tmp3(N),tp1[N],tp2[N],tp3[N];
int down[3][N];//down[0]表示正下方 down[1]左下来 down[2]表示右下来
inline void insert1(int x,int y,int z){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;
    data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;
}
void dfs(int x){
    if (x==1) return;
    int y=from1[x][0];dfs(from[y][0]);if (y==x) {printf("%d ",tr[x].id);return;}
    if (y<x){
        int now=y;while(bl[now]==bl[y]) printf("%d ",tr[now].id),--now;now=y+1;
        while(now<=x) printf("%d ",tr[now].id),++now; return;
    }
    int now=y;while(bl[now]==bl[y]) printf("%d ",tr[now].id),++now;now=y-1;
    while(now>=x) printf("%d ",tr[now].id),--now; return;
}
void dfs1(int x){
    for (int i=0;i<from1[x].size();++i){
        int y=from1[x][i];if (flag[y]) continue;flag[y]=1;
        for (int j=0;j<from[y].size();++j){
            int to=from[y][j];insert1(to,y,inf-1);d[to]--;d[y]++;out[to]++;dfs1(to);
        }
    }
}
inline bool bfs(){
    memset(level,0,sizeof(level));level[S]=1;queue<int>q;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;
            if (level[y]||!z) continue;level[y]=level[x]+1;q.push(y);if (y==T) return 1;
        }
    }return 0;
}
inline int dfsx(int x,int s){
    if (x==T) return s;int ss=s,tt=h[x];
    for (int i=cur[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;
        if(level[x]+1==level[y]&&z){
            int xx=dfsx(y,min(s,z));if (!xx) level[y]=0;tt=i;
            s-=xx;data[i].z-=xx;data[i^1].z+=xx;if(!s) {cur[x]=i;return ss;}
        }
    }cur[x]=tt;return ss-s;
}
int main(){
    freopen("2304.in","r",stdin);
//  freopen("aa.out","w",stdout);
    tr[1].x=0;tr[1].y=0;
    n=read();++n;for (int i=2;i<=n;++i) tr[i].x=read(),tr[i].y=read(),tr[i].id=i-1;
    sort(tr+1,tr+n+1,cmp);int last_line=-1,tot=1;t=n+1;S=n+2;T=n+3;
    for (int i=1;i<=n;++i){
        if (tr[i].y>last_line) line_to[tot-1]=i-1,line_from[tot]=i,++tot,last_line=tr[i].y;bl[i]=tot-1;
    }tot--;line_to[tot]=n;//for (int i=1;i<=tot;++i) printf("%d %d\n",line_from[i],line_to[i]);
    memset(dp,128,sizeof(dp));//for (int i=1;i<=n;++i) printf("%d ",bl[i]);printf("\n");
    for (int i=1;i<=n;++i) tmp1.push_back(tr[i].x),tmp2.push_back(tr[i].x-tr[i].y),tmp3.push_back(tr[i].x+tr[i].y);
    sort(tmp1.begin(),tmp1.end());sort(tmp2.begin(),tmp2.end());sort(tmp3.begin(),tmp3.end());
    int n1=unique(tmp1.begin(),tmp1.end())-tmp1.begin(),n2=unique(tmp2.begin(),tmp2.end())-tmp2.begin(),n3=unique(tmp3.begin(),tmp3.end())-tmp3.begin();
    int x=lower_bound(tmp1.begin(),tmp1.begin()+n1,tr[1].x)-tmp1.begin()+1, 
        x1=lower_bound(tmp2.begin(),tmp2.begin()+n2,tr[1].x-tr[1].y)-tmp2.begin()+1,
        x2=lower_bound(tmp3.begin(),tmp3.begin()+n3,tr[1].x+tr[1].y)-tmp3.begin()+1;
    tp1[x].push_back(1);tp2[x1].push_back(1);tp3[x2].push_back(1);
    for (int i=2;i<=n;++i){
        x=lower_bound(tmp1.begin(),tmp1.begin()+n1,tr[i].x)-tmp1.begin()+1, 
        x1=lower_bound(tmp2.begin(),tmp2.begin()+n2,tr[i].x-tr[i].y)-tmp2.begin()+1,
        x2=lower_bound(tmp3.begin(),tmp3.begin()+n3,tr[i].x+tr[i].y)-tmp3.begin()+1;
        if (!tp1[x].empty()) down[0][i]=tp1[x][tp1[x].size()-1];
        if (!tp2[x1].empty()) down[1][i]=tp2[x1][tp2[x1].size()-1];
        if (!tp3[x2].empty()) down[2][i]=tp3[x2][tp3[x2].size()-1];
        tp1[x].push_back(i);tp2[x1].push_back(i);tp3[x2].push_back(i);
    }
    dp[1]=0;
    for (int i=2;i<=tot;++i){
        for (int j=line_from[i];j<=line_to[i];++j) {
            int tmp=-inf;for (int z=0;z<=2;++z) {
                if (dp[down[z][j]]>tmp) from[j].clear(),from[j].push_back(down[z][j]),tmp=dp[down[z][j]];
                else if (tmp==dp[down[z][j]]) from[j].push_back(down[z][j]);
                if (tmp==-inf) continue;dp[j]=tmp+1;
            }
        } 
        memset(pre,-1,sizeof(pre));memset(last,-1,sizeof(last));int size=line_to[i]-line_from[i]+1;
        for (int j=line_from[i],cnt1=1;j<=line_to[i];++j,++cnt1) pre[cnt1]=max(pre[cnt1-1],dp[j]),dp1[j]=dp[j];
        for (int j=line_to[i],cnt1=size;j>=line_from[i];--j,--cnt1) last[cnt1]=max(last[cnt1+1],dp[j]);
        for (int j=line_from[i],cnt1=1;j<=line_to[i];++j,++cnt1){
            int max1=dp1[j],max2=-1;if (pre[cnt1-1]!=-1&&pre[cnt1-1]+cnt1-1>max1) max1=pre[cnt1-1]+cnt1-1,max2=pre[cnt1-1];
            if(last[cnt1+1]!=-1&&last[cnt1+1]+size-cnt1>max1) max1=last[cnt1+1]+size-cnt1,max2=last[cnt1+1];
            for (int z=line_from[i];z<j;++z) if (max1==dp1[z]+cnt1-1) from1[j].push_back(z);
            for (int z=j+1;z<=line_to[i];++z) if (max1==dp1[z]+size-cnt1) from1[j].push_back(z);
            if (max1==dp1[j]&&max1!=-2139062144) from1[j].push_back(j);dp[j]=max1;
        }
    }int ans=0,now=0,tt=0;
    for (int i=1;i<=n;++i) if (dp[i]>ans) ans=dp[i],now=i;printf("%d\n",ans);dfs(now);
    for (int i=2;i<=n;++i) if (dp[i]==dp[now]) dfs1(i),++tt;int sum=0;printf("\n");
    for (int i=1;i<=n;++i) {if (d[i]<0) insert1(i,T,-d[i]);if (d[i]>0) insert1(S,i,d[i]),sum+=d[i];insert1(0,i,inf);if (!out[i]) insert1(i,t,inf);}
    int ans1=0;while(bfs()) memcpy(cur,h,sizeof(cur)),ans1+=dfsx(S,inf);printf("%d",sum-ans1);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值