1. 程式人生 > >[BZOJ4200][NOI2015]小園丁與老司機

[BZOJ4200][NOI2015]小園丁與老司機

operator getch opera 記錄 online ron pro print 繼續

bzoj
luogu
loj上可以交部分分程序

description

小園丁 Mr. S 負責看管一片田野,田野可以看作一個二維平面。田野上有 \(n\) 棵許願樹,編號 \(1,2,3, \ldots ,n\),每棵樹可以看作平面上的一個點,其中第 \(i\) 棵樹 (\(1 \leq i \leq n\)) 位於坐標 \((x_i,y_i)\)。任意兩棵樹的坐標均不相同。

老司機 Mr. P 從原點 \((0,0)\) 駕車出發,進行若幹輪行動。每一輪,Mr. P 首先選擇任意一個滿足以下條件的方向:

  1. 為左、右、上、左上 \(45^{\circ}\) 、右上 \(45^{\circ}\) 五個方向之一。
  2. 沿此方向前進可以到達一棵他尚未許願過的樹。

完成選擇後,Mr. P 沿該方向直線前進,必須到達該方向上距離最近的尚未許願的樹,在樹下許願並繼續下一輪行動。如果沒有滿足條件的方向可供選擇,則停止行動。他會采取最優策略,在盡可能多的樹下許願。若最優策略不唯一,可以選擇任意一種。

不幸的是,小園丁 Mr. S 發現由於田野土質松軟,老司機 Mr. P 的小汽車在每輪行進過程中,都會在田野上留下一條車轍印,一條車轍印可看作以兩棵樹(或原點和一棵樹)為端點的一條線段。

在 Mr. P 之後,還有很多許願者計劃駕車來田野許願,這些許願者都會像 Mr. P 一樣任選一種最優策略行動。Mr. S 認為非左右方向(即上、左上\(45^{\circ}\)

、右上 \(45^{\circ}\) 三個方向)的車轍印很不美觀,為了維護田野的形象,他打算租用一些軋路機,在這群許願者到來之前夯實所有「可能留下非左右方向車轍印」的地面。

「可能留下非左右方向車轍印」的地面應當是田野上的若幹條線段,其中每條線段都包含在某一種最優策略的行進路線中。每臺軋路機都采取滿足以下三個條件的工作模式:

  1. 從原點或任意一棵樹出發。
  2. 只能向上、左上 \(45^{\circ}\) 、右上 \(45^{\circ}\) 三個方向之一移動,並且只能在樹下改變方向或停止。
  3. 只能經過「可能留下非左右方向車轍印」的地面,但是同一塊地面可以被多臺軋路機經過。

現在 Mr. P 和 Mr. S 分別向你提出了一個問題:

  1. 請給 Mr .P 指出任意一條最優路線。
  2. 請告訴 Mr. S 最少需要租用多少臺軋路機。

sol

這道題又是個二合一啊。
對於第一問,為老司機求出一條最優路徑,這個我們可以\(dp\)得到。
因為行走路線不能向下,我們令\(y_i\)相同的樹為同一層,可以發現最優決策一定是:先在同一層中把所有可以走到的樹全部走到,再向上走到達下一層。這麽一來每一層就會有一個確定的入點和出點。(這裏註意一下並不是所有層都會被經過)
先求出每個點向下、左下、右下三個方向到達的最近的樹,這是這個點作為這一層的進入點的三個前驅。設\(f_i\)表示到達第\(i\)棵樹作為當前這一層的出點時最多經過多少棵樹,那麽就可以枚舉入點,分三種情況討論:
1、入點就是出點,那麽這一層就只能經過這一棵樹。
2、入點在出點的左側,那麽可以經過出點左側的所有樹(在這一層中先一直往左走再一直往右走直到到達出點)
3、入點在出點的右側,那麽可以經過出點右側的所有樹。
然後這個\(dp\)是可以做到\(O(n)\)的。輸出方案的時候,其實可以不記錄\(dp\)轉移的前驅,只要照著轉移的方式再掃一遍所有前驅,看看是從哪個位置轉移來的即可。

接下來就需要處理小園丁的問題。我們先可以求出那些邊是必須經過的邊,然後問題就轉化為了要求這些邊的流量都至少是\(1\)
這是一個上下界網絡流的問題。我們先假設這些邊的流量是\(1\),這樣有的點就會不滿足流量守恒。新建源點匯點對這些流量不平衡的點進行補流或者是分流。
因為求的是最小流,按照最小流的一般思路是先從新建源點向新建匯點跑最大流,在不建立從原匯點到原源點的\(inf\)的情況下,這樣跑出的最大流就是原圖中最多的可以承擔的流量,所以答案就是\(\sum_{d_i>0}d_i\)-最大流。

code

碼農題啊qwq

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define cmax(a,b) (a<b?a=b:a)
const int N = 5e4+5;
const int inf = 1e9;
struct node{
    int x,y,id;
    bool operator < (const node &b) const{
        if (y==b.y) return x<b.x;return y<b.y;
    }
}p[N];
int n,o[N],len,d[N],ld[N],rd[N],f[N],g[N],tag[N],ans;
vector<int>v[N];map<int,int>M;queue<int>Q;
struct network{
    struct edge{int to,nxt,w;}a[N*100];
    int S,T,head[N],cnt,tot,du[N],dep[N],cur[N];
    void link(int u,int v){
        for (int e=head[u];e;e=a[e].nxt) if (a[e].to==v) return;
        a[++cnt]=(edge){v,head[u],inf};head[u]=cnt;
        a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
        ++du[v];--du[u];tag[u]=1;
    }
    void link(int u,int v,int w){
        a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
        a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
    }
    bool bfs(){
        memset(dep,0,sizeof(dep));
        dep[S]=1;Q.push(S);
        while (!Q.empty()){
            int u=Q.front();Q.pop();
            for (int e=head[u];e;e=a[e].nxt)
                if (a[e].w&&!dep[a[e].to])
                    dep[a[e].to]=dep[u]+1,Q.push(a[e].to);
        }
        return dep[T];
    }
    int dfs(int u,int f){
        if (u==T) return f;
        for (int &e=cur[u];e;e=a[e].nxt)
            if (a[e].w&&dep[a[e].to]==dep[u]+1){
                int tmp=dfs(a[e].to,min(a[e].w,f));
                if (tmp) {a[e].w-=tmp;a[e^1].w+=tmp;return tmp;}
            }
        return 0;
    }
    int dinic(){
        int res=0;
        while (bfs()){
            for (int i=0;i<=T;++i) cur[i]=head[i];
            while (int tmp=dfs(S,inf)) res+=tmp;
        }
        return res;
    }
    int work(){
        S=n+1;T=n+2;
        for (int i=0;i<=n;++i){
            if (du[i]>0) link(S,i,du[i]),tot+=du[i];
            if (du[i]<0) link(i,T,-du[i]);
        }
        return tot-dinic();
    }
}G;
bool cmp(node i,node j){return i.id<j.id;}
int pre(int x){
    if (~d[x]&&g[x]==f[d[x]]) return d[x];
    if (~ld[x]&&g[x]==f[ld[x]]) return ld[x];
    if (~rd[x]&&g[x]==f[rd[x]]) return rd[x];
}
void dfs(int u){
    if (!u) return;int sz=v[p[u].y].size(),pos=0;
    while (v[p[u].y][pos]!=u) ++pos;
    if (f[u]==g[u]+1) {dfs(pre(u));printf("%d ",u);return;}
    for (int i=0;i<pos;++i){
        int x=v[p[u].y][i];
        if (f[u]==g[x]+pos+1){
            dfs(pre(x));
            for (int j=i;~j;--j) printf("%d ",v[p[u].y][j]);
            for (int j=i+1;j<=pos;++j) printf("%d ",v[p[u].y][j]);
            return;
        }
    }
    for (int i=pos+1;i<sz;++i){
        int x=v[p[u].y][i];
        if (f[u]==g[x]+sz-pos){
            dfs(pre(x));
            for (int j=i;j<sz;++j) printf("%d ",v[p[u].y][j]);
            for (int j=i-1;j>=pos;--j) printf("%d ",v[p[u].y][j]);
            return;
        }
    }
}
void build(int u){
    int sz=v[p[u].y].size(),pos=0;
    while (v[p[u].y][pos]!=u) ++pos;
    if (f[u]==g[u]+1){
        if (~d[u]&&g[u]==f[d[u]]) G.link(d[u],u);
        if (~ld[u]&&g[u]==f[ld[u]]) G.link(ld[u],u);
        if (~rd[u]&&g[u]==f[rd[u]]) G.link(rd[u],u);
    }
    for (int i=0;i<pos;++i){
        int x=v[p[u].y][i];
        if (f[u]==g[x]+pos+1){
            if (~d[x]&&g[x]==f[d[x]]) G.link(d[x],x);
            if (~ld[x]&&g[x]==f[ld[x]]) G.link(ld[x],x);
            if (~rd[x]&&g[x]==f[rd[x]]) G.link(rd[x],x);
        }
    }
    for (int i=pos+1;i<sz;++i){
        int x=v[p[u].y][i];
        if (f[u]==g[x]+sz-pos){
            if (~d[x]&&g[x]==f[d[x]]) G.link(d[x],x);
            if (~ld[x]&&g[x]==f[ld[x]]) G.link(ld[x],x);
            if (~rd[x]&&g[x]==f[rd[x]]) G.link(rd[x],x);
        }
    }
}
int main(){
    n=gi();G.cnt=1;
    for (int i=1;i<=n;++i) p[i]=(node){gi(),gi(),i},o[i]=p[i].y;
    sort(o+1,o+n+1);len=unique(o+1,o+n+1)-o-1;sort(p+1,p+n+1);
    memset(d,-1,sizeof(d));M.clear();M[0]=0;
    for (int i=1;i<=n;++i){
        if (M.find(p[i].x)!=M.end()) d[p[i].id]=M[p[i].x];
        M[p[i].x]=p[i].id;
    }
    memset(ld,-1,sizeof(ld));M.clear();M[0]=0;
    for (int i=1;i<=n;++i){
        if (M.find(p[i].x+p[i].y)!=M.end()) ld[p[i].id]=M[p[i].x+p[i].y];
        M[p[i].x+p[i].y]=p[i].id;
    }
    memset(rd,-1,sizeof(rd));M.clear();M[0]=0;
    for (int i=1;i<=n;++i){
        if (M.find(p[i].y-p[i].x)!=M.end()) rd[p[i].id]=M[p[i].y-p[i].x];
        M[p[i].y-p[i].x]=p[i].id;
    }
    for (int i=1;i<=n;++i){
        p[i].y=lower_bound(o+1,o+len+1,p[i].y)-o;
        v[p[i].y].push_back(p[i].id);
    }
    memset(f,-63,sizeof(f));memset(g,-63,sizeof(g));f[0]=0;
    for (int i=1;i<=len;++i){
        int sz=v[i].size(),mx;
        for (int j=0;j<sz;++j){
            int x=v[i][j];
            if (~d[x]) cmax(g[x],f[d[x]]);
            if (~ld[x]) cmax(g[x],f[ld[x]]);
            if (~rd[x]) cmax(g[x],f[rd[x]]);
            cmax(f[x],g[x]+1);
        }
        mx=-inf;
        for (int j=0;j<sz;++j){
            int x=v[i][j];
            cmax(f[x],mx+j+1);cmax(mx,g[x]);
        }
        mx=-inf;
        for (int j=sz-1;~j;--j){
            int x=v[i][j];
            cmax(f[x],mx+sz-j);cmax(mx,g[x]);
        }
    }
    for (int i=1;i<=n;++i) cmax(ans,f[i]);
    printf("%d\n",ans);sort(p+1,p+n+1,cmp);
    for (int i=1;i<=n;++i) if (f[i]==ans) {dfs(i);puts("");break;}
    for (int i=1;i<=n;++i) if (f[i]==ans) tag[i]=1;
    for (int i=len;i;--i)
        for (int j=0,sz=v[i].size();j<sz;++j)
            if (tag[v[i][j]]) build(v[i][j]);
    printf("%d\n",G.work());
    return 0;
}

[BZOJ4200][NOI2015]小園丁與老司機