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

终于做完了NOI2015的所有题目qwq

码农题+细节题啊。。
首先正反DP求出所有的最优路径,同一行转移一定要写对。。正反的时候是不一样的,然后根据最优路径建图跑最小流就行了。。
成功在UOJ垫了一波大底。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#define inf 1000000007
#define N 50050
#define M 2000020
using namespace std;
int n,cnt=1,ans,tot,pos,S,T,SS,TT;
int head[N],dis[N],q[N],dp[N],mx[N],du[N],f[N],cur[N],g[N],from[N],stack[N],seq[N];
struct node {int x,y,id;} a[N];
int next[M],list[M],key[M];
bool tag[N];
map<int,int> mp1,mp2,mp3;
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline bool operator<(node a,node b)
{
    return a.y==b.y?a.x<b.x:a.y<b.y;
}
inline void insert(int x,int y,int z)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
    key[cnt]=z;
}
inline void DP1()
{
    for (int i=1;i<=n;i++) dp[i]=mx[i]=-inf;
    dp[0]=0;
    for (int i=0,t=0;i<=n;i=++t)
    {
        while (t<n&&a[t+1].y==a[t].y) t++;
        for (int j=i;j<=t;j++)
        {
            if (mp1.find(a[j].x+a[j].y)!=mp1.end())
            {
                int p=mp1[a[j].x+a[j].y];
                if (dp[p]+1>dp[j]) dp[j]=dp[p]+1,g[j]=p;
            }
            mp1[a[j].x+a[j].y]=j;
            if (mp2.find(a[j].x-a[j].y)!=mp2.end())
            {
                int p=mp2[a[j].x-a[j].y];
                if (dp[p]+1>dp[j]) dp[j]=dp[p]+1,g[j]=p;
            }
            mp2[a[j].x-a[j].y]=j;
            if (mp3.find(a[j].x)!=mp3.end())
            {
                int p=mp3[a[j].x];
                if (dp[p]+1>dp[j]) dp[j]=dp[p]+1,g[j]=p;
            }
            mp3[a[j].x]=j;
        }
        int now=-1;
        for (int j=i+1;j<=t;j++)
        {
            if (now==-1||dp[j-1]>dp[now]) now=j-1;
            if (dp[now]+j-i>mx[j]) mx[j]=dp[now]+j-i,from[j]=now;
        }
        now=-1;
        for (int j=t-1;j>=i;j--)
        {
            if (now==-1||dp[j+1]>dp[now]) now=j+1;
            if (dp[now]+t-j>mx[j]) mx[j]=dp[now]+t-j,from[j]=now;
        }
        for (int j=i;j<=t;j++)
            if (mx[j]>dp[j]) dp[j]=mx[j],tag[j]=1;
    }
    pos=0;
    for (int i=1;i<=n;i++)
        if (dp[i]>dp[pos]) pos=i;
    ans=dp[pos];
    printf("%d\n",ans);
}
inline void DP2()
{
    mp1.clear(); mp2.clear(); mp3.clear();
    for (int i=1;i<=n;i++)
        if (dp[i]==ans) f[i]=1; else f[i]=0;
    for (int i=1;i<=n;i++) mx[i]=-inf;
    for (int i=n,t=n;~i;i=--t)
    {
        while (t&&a[t-1].y==a[t].y) t--;
        for (int j=t;j<=i;j++)
        {
            if (mp1.find(a[j].x+a[j].y)!=mp1.end())
            {
                int p=mp1[a[j].x+a[j].y];
                if (f[p]+1>f[j]) f[j]=f[p]+1;
            }
            mp1[a[j].x+a[j].y]=j;
            if (mp2.find(a[j].x-a[j].y)!=mp2.end())
            {
                int p=mp2[a[j].x-a[j].y];
                if (f[p]+1>f[j]) f[j]=f[p]+1;
            }
            mp2[a[j].x-a[j].y]=j;
            if (mp3.find(a[j].x)!=mp3.end())
            {
                int p=mp3[a[j].x];
                if (f[p]+1>f[j]) f[j]=f[p]+1;
            }
            mp3[a[j].x]=j;
        }
        int now=-1;
        for (int j=t+1;j<=i;j++)
        {
            if (now==-1||f[j-1]+i-j+1>f[now]+i-now) now=j-1;
            if (f[now]+i-now>mx[j]) mx[j]=f[now]+i-now;
        }
        now=-1;
        for (int j=i-1;j>=t;j--)
        {
            if (now==-1||f[j+1]+j+1-t>f[now]+now-t) now=j+1;
            if (f[now]+now-t>mx[j]) mx[j]=f[now]+now-t;
        }
        for (int j=t;j<=i;j++)
            if (mx[j]>f[j]) f[j]=mx[j];
    }
}
inline int findl(int x)
{
    int l=1,r=n,ans;
    while (l<=r)
    {
        int mid=l+r>>1;
        if (a[mid].y>=x) ans=mid,r=mid-1; else l=mid+1;
    }
    return ans;
}
inline int findr(int x)
{
    int l=1,r=n,ans;
    while (l<=r)
    {
        int mid=l+r>>1;
        if (a[mid].y<=x) ans=mid,l=mid+1; else r=mid-1;
    }
    return ans;
}
inline void print(int x,int y)
{
    int l=findl(a[x].y),r=findr(a[x].y);
    if (x<y)
    {
        for (int i=x;i>=l;i--) seq[++tot]=i;
        for (int i=x+1;i<=y;i++) seq[++tot]=i;
    }
    else
    {
        for (int i=x;i<=r;i++) seq[++tot]=i;
        for (int i=x-1;i>=y;i--) seq[++tot]=i;
    }
}
inline void solve()
{
    int top=0; tot=0;
    while (pos)
        if (tag[pos])
            stack[++top]=pos,stack[++top]=from[pos],pos=g[from[pos]];
        else stack[++top]=pos,pos=g[pos];
    while (top)
        if (top>1&&a[stack[top]].y==a[stack[top-1]].y)
            print(stack[top],stack[top-1]),top-=2;
        else seq[++tot]=stack[top--];
    for (int i=1;i<=tot;i++) printf("%d ",a[seq[i]].id); 
    puts("");
}
inline bool BFS()
{
    int t=0,w=1,x;
    memset(dis,-1,sizeof(dis));
    q[1]=SS; dis[SS]=1;
    while (t<w)
    {
        x=q[++t];
        for (int i=head[x];i;i=next[i])
            if (key[i]&&dis[list[i]]==-1)
                dis[q[++w]=list[i]]=dis[x]+1;
    }
    return dis[TT]!=-1;
}
int find(int x,int flow)
{
    if (x==TT) return flow;
    int w,used=0;
    for (int i=cur[x];i;i=next[i])
        if (key[i]&&dis[list[i]]==dis[x]+1)
        {
            w=find(list[i],min(key[i],flow-used));
            key[i]-=w; key[i^1]+=w; used+=w;
            if (key[i]) cur[x]=i;
            if (used==flow) return used;
        }
    if (!used) dis[x]=-1;
    return used;
}
inline void dinic()
{
    int tmp=0;
    while (BFS())
    {
        for (int i=0;i<=TT;i++) cur[i]=head[i];
        tmp+=find(SS,inf);
    }
}
inline void build()
{
    S=n+1; T=n+2; SS=T+1; TT=T+2;
    mp1.clear(); mp2.clear(); mp3.clear();
    for (int i=n;~i;i--)
    {
        if (mp1.find(a[i].x+a[i].y)!=mp1.end())
        {
            int p=mp1[a[i].x+a[i].y];
            if (dp[i]+f[p]==ans) 
                insert(i,p,inf),insert(p,i,0),du[i]--,du[p]++;
        }
        mp1[a[i].x+a[i].y]=i;
        if (mp2.find(a[i].x-a[i].y)!=mp2.end())
        {
            int p=mp2[a[i].x-a[i].y];
            if (dp[i]+f[p]==ans)
                insert(i,p,inf),insert(p,i,0),du[i]--,du[p]++;
        }
        mp2[a[i].x-a[i].y]=i;
        if (mp3.find(a[i].x)!=mp3.end())
        {
            int p=mp3[a[i].x];
            if (dp[i]+f[p]==ans)
                insert(i,p,inf),insert(p,i,0),du[i]--,du[p]++;
        }
        mp3[a[i].x]=i;
    }
    for (int i=0;i<=n;i++)
        insert(S,i,inf),insert(i,S,0),insert(i,T,inf),insert(T,i,0);
    for (int i=0;i<=n;i++)
        if (du[i]>0) insert(SS,i,du[i]),insert(i,SS,0);
        else if (du[i]<0) insert(i,TT,-du[i]),insert(TT,i,0);
}
int main()
{
    n=read();
    for (int i=1;i<=n;i++)
        a[i].x=read(),a[i].y=read(),a[i].id=i;
    sort(a+1,a+n+1);
    DP1(); 
    DP2();
    solve();
    build();
    dinic();
    insert(T,S,inf); insert(S,T,0);
    dinic();
    cout << key[cnt] << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值