4200: [Noi2015]小园丁与老司机
Time Limit: 20 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 139 Solved: 80
[ Submit][ Status][ Discuss]
Description
Input
输入文件的第 1 行包含 1 个正整数 n,表示许愿树的数量。
Output
Sample Input
-1 1
1 1
-2 2
0 8
0 9
0 10
Sample Output
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)。
HINT
Source
dp + 有上下界的最小流,细节非常非常非常多。。。。。搞了整整一上午。。。
先考虑前两问,,老司机的开车方向只能向纵坐标增大,或者是左右开,因而可以分开dp
定义g[i]:第i个点,由y小于yi的点转移而来,最大值
那么对于每个i,从下往上走到i的方向只有三个,找出这三个方向上距离i最近的点,直接转移就行了
定义f[i]:第i个点,由左边或者右边开过来的最大值
对于f[i]的转移,首先让所有可能的路径开到当前这一层,也就是处理好该层所有的g[i]
贪心地想,如果要从同层的i开到j,假设i在j左边,那么最好的方案,就是i先开到最左端,然后慢慢往右开
反过来也是一样的,因此对于每层的f[i],从左往右从右往左分别用g[i]更新一次就行了
因而对于g[i]的转移,其实是要同时考虑三个方向上的点的f[i]和g[i]
最后检查一下所有点的f[i],g[i],就能得出第一问了。
现在要找出路径,任取一个合法的终点,每次判断它是属于哪一类(同层转移或者下层往上得到)
然后在符合的转移集合直接枚举上一个点,再继续搜索,要注意同层转移的时候把中间点加入
这样就解决了前两问,对于第三问,建模如下
倒着dp一次,和找路径的方式一样,处理出所有可能成为最短路径上的边,同层边忽略
对于从下往上的边(i,j),从i向j连一条下界为1,上界为INF的边
对于所有可能在最短路上的点,从s到它连一条INF的边,它到t连一条INF的边
也就是说,可以无限从这个点进入,无限在这个点停止
对于该网络的最小可行流就是第三问的答案
怎么求最小流?在网上搜了很久很久,,确定了时间复杂度低又好实现的模板
先构造成循环流,但先不添加(t,s,INF)这条边
然后跑一遍超级源汇最大流,再添加(t,s,INF),跑第二遍超级源汇最大流,这次跑出的最大流就是原图最小流了
最后,,此题细节很多。。
网上看到有人实现的时候,说题目给定每层的点不超过1k,因而每层直接O(N^2)暴力找路
然后又说只T第7个点。其实第7个点根本没有每层不超过1k点的限制的
因此,我处理的时候,是开一个vis数组,判断哪些点有没有被加入最短路集合
这样dp就可以写成BFS的模式,但是不能同时找随机路径(因为路径的点可能在入集合前就被经过了)
总之码农题都是好麻烦的。。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
const int maxn = 5E4 + 50;
const int maxm = 2E6 + 20;
const int INF = ~0U>>1;
struct E{
int to,cap,flow; E(){}
E(int to,int cap,int flow): to(to),cap(cap),flow(flow){}
}edgs[maxm];
struct P{
int x,y; P(){}
P(int x,int y): x(x),y(y){}
}Point[maxn];
struct data{
int Num,typ; data(){}
data(int Num,int typ): Num(Num),typ(typ){}
};
struct d2{
int x,y; d2(){}
d2(int x,int y): x(x),y(y){}
bool operator < (const d2 &B) const
{
if (x < B.x) return 1;
if (x > B.x) return 0;
return y < B.y;
}
};
int n,cur = 1,cnt,tp,s,t,S,T,sum,Ans,a[maxn],in[maxn],out[maxn]
,Cur[maxn],f[maxn],g[maxn],pos[maxn],Road[maxn],L[maxn];
bool in_road[maxn],vis[maxn][2],Reach[maxn][2],Typ[maxn];
vector <int> v[maxn],p[maxn],G[maxn];
queue <int> Q;
queue <data> Q2;
map <int,int> ma,mb,mc;
map <d2,bool> Edgs;
bool cmp(const int &x,const int &y) {return Point[x].x < Point[y].x;}
void Add(int x,int y,int cap)
{
v[x].push_back(cnt); edgs[cnt++] = E(y,cap,0);
v[y].push_back(cnt); edgs[cnt++] = E(x,0,0);
}
int Tree(const int &x,const int &y,const int &siz)
{
return x < y?y:siz - 1 - y;
}
int getint()
{
char ch = getchar(); int ret = 0,A = 1;
while (ch < '0' || '9' < ch)
{
if (ch == '-') A = -1;
ch = getchar();
}
while ('0' <= ch && ch <= '9')
ret = ret*10 + ch - '0',ch = getchar();
return ret*A;
}
void Pre_Work()
{
n = getint();
for (int i = 1; i <= n; i++)
{
int x = getint(),y = getint();
Point[i] = P(x,y); a[i] = y;
}
Point[++n] = P(0,0); sort(a + 1,a + n + 1);
for (int i = 2; i <= n; i++)
if (a[i] != a[i-1]) a[++cur] = a[i];
for (int i = 1; i <= n; i++)
{
int pos = lower_bound(a + 1,a + cur + 1,Point[i].y) - a;
p[pos].push_back(i);
}
}
void Dp()
{
ma[0] = n; mb[0] = n; mc[0] = n; Reach[n][0] = Reach[n][1] = 1;
for (int i = 2; i <= cur; i++)
{
int siz = p[i].size(); sort(p[i].begin(),p[i].end(),cmp);
for (int j = 0; j < siz; j++)
{
int now = p[i][j]; P o = Point[now]; pos[now] = j;
if (ma.count(o.x))
{
int Pre = ma[o.x]; int w = max(f[Pre],g[Pre]);
g[now] = max(g[now],w + 1);
G[now].push_back(Pre); Reach[now][1] = 1;
}
if (mb.count(o.x - o.y))
{
int Pre = mb[o.x-o.y]; int w = max(f[Pre],g[Pre]);
g[now] = max(g[now],w + 1);
G[now].push_back(Pre); Reach[now][1] = 1;
}
if (mc.count(o.x + o.y))
{
int Pre = mc[o.x+o.y]; int w = max(f[Pre],g[Pre]);
g[now] = max(g[now],w + 1);
G[now].push_back(Pre); Reach[now][1] = 1;
}
}
int Max = Reach[p[i][0]][1]?g[p[i][0]]:-INF;
for (int j = 1; j < siz; j++)
{
int now = p[i][j];
if (Max != -INF)
f[now] = Max + 1,Reach[now][0] = 1;
if (Reach[now][1])
Max = max(Max + 1,g[now] + j);
else if (Max != -INF) ++Max;
}
Max = Reach[p[i][siz-1]][1]?g[p[i][siz-1]]:-INF;
for (int j = siz - 2; j >= 0; j--)
{
int now = p[i][j];
if (Max != -INF)
f[now] = max(f[now],Max + 1),Reach[now][0] = 1;
if (Reach[now][1])
Max = max(Max + 1,g[now] + siz - 1 - j);
else if (Max != -INF) ++Max;
}
for (int j = 0; j < siz; j++)
{
int now = p[i][j]; P o = Point[now];
if (Reach[now][0] || Reach[now][1])
ma[o.x] = mb[o.x-o.y] = mc[o.x+o.y] = now;
}
}
}
void Push_Road(int i,int k,int siz,int Y)
{
if (i < pos[k])
{
for (int j = pos[k] - 1; j > i; j--)
Road[++tp] = p[Y][j];
for (int j = 0; j <= i; j++)
Road[++tp] = p[Y][j];
Typ[tp] = 1;
}
else
{
for (int j = pos[k] + 1; j < i; j++)
Road[++tp] = p[Y][j];
for (int j = siz - 1; j >= i; j--)
Road[++tp] = p[Y][j];
Typ[tp] = 1;
}
}
void Dfs_Road(int x,int typ)
{
Road[++tp] = x;
if (typ)
{
for (int i = 0; i < G[x].size(); i++)
{
int to = G[x][i]; bool flag = 0,Nex_typ;
if (Reach[to][0] && f[to] + 1 == g[x])
flag = 1,Nex_typ = 0;
if (Reach[to][1] && g[to] + 1 == g[x])
flag = 1,Nex_typ = 1;
if (flag)
{
Dfs_Road(to,Nex_typ); return;
}
}
}
else
{
int Y = lower_bound(a + 1,a + cur + 1,Point[x].y) - a,siz = p[Y].size();
for (int i = 0; i < siz; i++)
if (i != pos[x])
{
int to = p[Y][i];
if (Reach[to][1] && g[to] + Tree(i,pos[x],siz) == f[x])
{
Push_Road(i,x,siz,Y),--tp;
Dfs_Road(Road[tp+1],1); return;
}
}
}
}
void Build_Graph()
{
for (int i = 1; i <= n; i++)
{
if (f[i] == Ans)
{
vis[i][0] = 1; Q2.push(data(i,0));
if (!tp) Dfs_Road(i,0);
}
if (g[i] == Ans)
{
vis[i][1] = 1; Q2.push(data(i,1));
if (!tp) Dfs_Road(i,1);
}
}
while (!Q2.empty())
{
int k = Q2.front().Num,typ = Q2.front().typ; Q2.pop(); in_road[k] = 1;
if (typ)
{
for (int i = 0; i < G[k].size(); i++)
{
int to = G[k][i]; bool flag = 0;
if (Reach[to][0] && f[to] + 1 == g[k])
{
if (!vis[to][0]) vis[to][0] = 1,Q2.push(data(to,0)); flag = 1;
}
if (Reach[to][1] && g[to] + 1 == g[k])
{
if (!vis[to][1]) vis[to][1] = 1,Q2.push(data(to,1)); flag = 1;
}
if (flag && !Edgs.count(d2(to,k)))
++in[k],++out[to],Add(to,k,INF),Edgs[d2(to,k)] = 1;
}
}
else
{
int Y = lower_bound(a + 1,a + cur + 1,Point[k].y) - a;
int siz = p[Y].size();
for (int i = 0; i < siz; i++)
if (i != pos[k])
{
int to = p[Y][i];
if (Reach[to][1] && g[to] + Tree(i,pos[k],siz) == f[k])
{
if (!vis[to][1]) vis[to][1] = 1,Q2.push(data(to,1));
}
}
}
}
for (int i = 1; i <= n; i++)
if (in_road[i])
{
Add(s,i,INF); Add(i,t,INF);
int du = in[i] - out[i];
if (du > 0) Add(S,i,du),sum += du;
else if (du < 0) Add(i,T,-du);
}
}
int Dfs(int x,int A)
{
if (x == T) return A; int flow = 0;
for (int &i = Cur[x]; i < v[x].size(); i++)
{
E &e = edgs[v[x][i]];
if (e.cap == e.flow || L[e.to] != L[x] + 1) continue;
int f = Dfs(e.to,min(A,e.cap - e.flow));
if (!f) continue; flow += f; e.flow += f;
edgs[v[x][i]^1].flow -= f; A -= f;
if (!A) return flow;
}
if (!flow) L[x] = -1; return flow;
}
bool BFS()
{
for (int i = 1; i <= T; i++) L[i] = 0;
L[S] = 1; Q.push(S);
while (!Q.empty())
{
int k = Q.front(); Q.pop();
for (int i = 0; i < v[k].size(); i++)
{
E e = edgs[v[k][i]];
if (e.cap == e.flow || L[e.to]) continue;
L[e.to] = L[k] + 1; Q.push(e.to);
}
}
return L[T];
}
int Dinic()
{
int MaxFlow = 0;
while (BFS())
{
for (int i = 1; i <= T; i++) Cur[i] = 0;
MaxFlow += Dfs(S,INF);
}
return MaxFlow;
}
int main()
{
Pre_Work(); Dp();
for (int i = 1; i <= n; i++) Ans = max(Ans,max(f[i],g[i]));
cout << Ans << endl; s = n + 1; t = s + 1; S = t + 1; T = S + 1;
Build_Graph();
for (int i = tp - 1; i > 1; i--)
printf("%d ",Road[i]); cout << Road[1] << endl;
//if (tp - 1 != Ans) puts("Wrong Answer!"); else puts("Accepted");
Dinic(); Add(t,s,INF); cout << Dinic();
return 0;
}