BZOJ 4200: [Noi2015]小园丁与老司机 Dp+有源汇上下界最小流

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

title

BZOJ 4200
LUOGU 2304
Description

小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面。田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2,3,…,n,每棵树可以看作平面上的一个点,其中第 ii 棵树 (1≤i≤n1≤i≤n) 位于坐标 (xi,yi)(xi,yi)。任意两棵树的坐标均不相同。
老司机 Mr. P 从原点 (0,0)(0,0) 驾车出发,进行若干轮行动。每一轮,Mr. P 首先选择任意一个满足以下条件的方向:
为左、右、上、左上 45∘45∘ 、右上 45∘45∘ 五个方向之一。
沿此方向前进可以到达一棵他尚未许愿过的树。
完成选择后,Mr. P 沿该方向直线前进,必须到达该方向上距离最近的尚未许愿的树,在树下许愿并继续下一轮行动。如果没有满足条件的方向可供选择,则停止行动。他会采取最优策略,在尽可能多的树下许愿。若最优策略不唯一,可以选择任意一种。
不幸的是,小园丁 Mr. S 发现由于田野土质松软,老司机 Mr. P 的小汽车在每轮行进过程中,都会在田野上留下一条车辙印,一条车辙印可看作以两棵树(或原点和一棵树)为端点的一条线段。
在 Mr. P 之后,还有很多许愿者计划驾车来田野许愿,这些许愿者都会像 Mr. P 一样任选一种最优策略行动。Mr. S 认为非左右方向(即上、左上 45∘45∘ 、右上 45∘45∘ 三个方向)的车辙印很不美观,为了维护田野的形象,他打算租用一些轧路机,在这群许愿者到来之前夯实所有“可能留下非左右方向车辙印”的地面。
“可能留下非左右方向车辙印”的地面应当是田野上的若干条线段,其中每条线段都包含在某一种最优策略的行进路线中。每台轧路机都采取满足以下三个条件的工作模式:
从原点或任意一棵树出发。
只能向上、左上 45∘45∘ 、右上 45∘45∘ 三个方向之一移动,并且只能在树下改变方向或停止。
只能经过“可能留下非左右方向车辙印”的地面,但是同一块地面可以被多台轧路机经过。
现在 Mr. P 和 Mr. S 分别向你提出了一个问题:
请给 Mr .P 指出任意一条最优路线。
请告诉 Mr. S 最少需要租用多少台轧路机。

Input

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

Output

输出文件包括 3 行。
输出文件的第 1 行输出 1 个整数 m,表示 Mr. P 最多能在多少棵树下许愿。
输出文件的第 2 行输出 m 个整数,相邻整数之间用单个空格隔开,表示 Mr. P 应该依次在哪些树下许愿。
输出文件的第 3 行输出 1 个整数,表示 Mr. S 最少需要租用多少台轧路机。

Sample Input

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

Sample Output

3
2 1 3
3
explanation
最优路线 2 条可许愿 3 次:(0,0)→(1,1)→(−1,1)→(−2,2)(0,0)→(1,1)→(−1,1)→(−2,2) 或 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。 至少 3 台轧路机,路线是 (0,0)→(1,1)(0,0)→(1,1),(−1,1)→(−2,2)(−1,1)→(−2,2) 和 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。

analysis

额,去yyb大佬那里吧,我现在还解释不清有源汇上下界最小流。。

这道题的 D P DP 也是够神仙的了。。

等我慢慢搞吧。。

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+10,maxm=maxn*20,inf=0x3f3f3f3f;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48,x/=10;
    while (num) putchar(ch[num--]);
}

///////////////////////////////////////////////////////////////////////////////////////////Here is flow
int ver[maxm],edge[maxm],Next[maxm],head[maxn],len=1;
inline void add(int x,int y,int z)
{
    ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
    ver[++len]=x,edge[len]=0,Next[len]=head[y],head[y]=len;
}

bool va[maxn][2];
int In[maxn],Out[maxn],ly[maxn],lx[maxn];
int py[maxn][1010],pr[maxn][5];
inline void build(int x,bool ty)
{
    if (!x || va[x][ty]) return ;
    va[x][ty]=1;
    if (ty) for (int i=1; i<=ly[x]; ++i) build(py[x][i],0);
    else for (int i=1; i<=lx[x]; ++i) { int y=pr[x][i]; ++Out[y],++In[x],build(y,1),add(y,x,inf); }
}

int s,t;
int dist[maxn];
inline bool bfs()
{
    queue<int>q;
    memset(dist,0,sizeof(dist));
    q.push(s),dist[s]=1;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=head[x]; i; i=Next[i])
        {
            int y=ver[i];
            if (edge[i] && !dist[y])
            {
                dist[y]=dist[x]+1;
                if (y==t) return 1;
                q.push(y);
            }
        }
    }
    return 0;
}

inline int get(int x,int low)
{
    if (x==t) return low;
    int tmp=low;
    for (int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if (edge[i] && dist[y]==dist[x]+1)
        {
            int a=get(y,min(tmp,edge[i]));
            if (!a) dist[y]=0;
            edge[i]-=a;
            edge[i^1]+=a;
            if (!(tmp-=a)) break;
        }
    }
    return low-tmp;
}
///////////////////////////////////////////////////////////////////////////////////////////Above is flow

///////////////////////////////////////////////////////////////////////////////////////////Here is Dp
struct Orz{int x,y,id;}a[maxn];
inline bool cmp(Orz a,Orz b)
{
    return a.y<b.y || (a.y==b.y && a.x<b.x);
}

int f[maxn],d[maxn];
inline void renew(int x,int &z)
{
    if (z)
    {
        if (f[x]<=d[z]) f[x]=d[z]+1,pr[x][ lx[x]=1 ]=z;
        else if (f[x]==d[z]+1) pr[x][ ++lx[x] ]=z;
    }
    z=x;
}

inline void Print(int x,bool ty)//输出方案
{
    if (!x) return ;
    if (ty)
    {
        int y=py[x][1];
        Print(y,0);
        if (x==y) return ;
        if (y<x)
        {
            for (int z=y-1; a[z].y==a[y].y; --z) write(a[z].id),putchar(' ');
            for (int z=y+1; z<=x; ++z) write(a[z].id),putchar(' ');
        }
        else
        {
            for (int z=y+1; a[z].y==a[y].y; ++z) write(a[z].id),putchar(' ');
            for (int z=y-1; z>=x; --z) write(a[z].id),putchar(' ');
        }
    }
    else Print(pr[x][1],1),write(a[x].id),putchar(' ');
}
///////////////////////////////////////////////////////////////////////////////////////////Above is Dp

map<int,int>m1,m2,mx;
int main()
{
    int n;read(n);
    for (int i=1; i<=n; ++i) read(a[i].x),read(a[i].y),a[i].id=i;
    sort(a+1,a+n+1,cmp);

    int ans=0;
    for (int i=1,lt=0; i<=n; i=lt+1)
    {
        while (lt<n && a[lt+1].y==a[i].y)
        {
            ++lt;
            int x=a[lt].x,y=a[lt].y;
            if (x==y || x==-y || !x) f[lt]=1,pr[lt][ lx[lt]=1 ]=0;
            else f[lt]=-n;
            renew(lt,mx[x]),renew(lt,m1[x+y]),renew(lt,m2[x-y]);
        }
        int w;
        for (int j=i; j<=lt; ++j)
        {
            w=f[j];
            py[j][ ly[j]=1 ]=j;
            for (int k=i; k<=j-1; ++k)
                if (f[k]+j-i>w) w=f[k]+j-i, py[j][ ly[j]=1 ]=k;
                else if (f[k]+j-i==w) py[j][ ++ly[j] ]=k;
            d[j]=w;
        }
        for (int j=lt; j>=i; --j)
        {
            w=d[j];
            for (int k=j+1; k<=lt; ++k)
                if (f[k]+lt-j>w) w=f[k]+lt-j, py[j][ ly[j]=1 ]=k;
                else if (f[k]+lt-j==w) py[j][ ++ly[j] ]=k;
            d[j]=w;
            ans=max(ans,w);
        }
    }
    write(ans),puts("");

    for (int i=1; i<=n; ++i)
        if (d[i]==ans) { Print(i,1),puts(""); break; }

    s=n+1,t=n+2;
    for (int i=1; i<=n; ++i)
        if (d[i]==ans) build(i,1);
    int sum=0;
    for (int i=0; i<=n; ++i)
        if (In[i]>Out[i]) add(s,i,In[i]-Out[i]),sum+=In[i]-Out[i];
        else add(i,t,Out[i]-In[i]);

    int res=0;
    while (bfs()) res+=get(s,inf);
    write(sum-res),puts("");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/91899171