[LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机
[LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机
题意
给定平面上的 \(n\) 个整点 \((x_i,y_i)\), 一共有两个问题.
第一个问题是从原点 \((0,0)\) 出发, 在只能向←↖↑↗→五个方向中有未到达的点的方向走且在没有到达一个点的时候不能中途转弯的情况下最多能到达的点数, 并输出一种可行方案.
第二个问题是如果用若干可以从任意点出发但是只能向↖↑↗方向沿着所有可能出现在最优解的直线上走的压路机将所有可能出现在最优解上的边都走过至少一遍所需要的最少的压路机数量.
\(n\le 50000,|x_i|\le 10^9,0<y_i\le 10^9\)
题解
农业题真tm劲啊
第一个问题相当于一个有特殊限制的分层图最长路.
显然走的时候 \(y\) 值是单调不降的, 我们可以从小到大枚举 \(y\) 坐标分层计算.
同一层的时候假设在 \(s\) 处进入, 从 \(t\) 处离开, 那么最优策略一定是先走到这一层中对 \(t\) 的对侧端点然后再回来. 我们设 \(f^{[1]}_i\) 为从原点走到 \(i\) 的最长路, \(f^{[2]}_i\) 为上一层中可以走到点 \(i\) 的点中最大的 \(f^{[1]}\), 即进入当前层之前的最长路.
因为↖↑↗三个方向能移动到的位置分别保持 \(x+y\), \(x\), \(x-y\) 相等, 于是可以全局维护一个哈希表或者 map
来维护 \(f^{[2]}_i\). 如果以前没有出现过相等位置则贡献为 \(-\infty\). 原点的贡献为 \(0\).
从 \(f^{[2]}\) 计算 \(f^{[1]}\) 则可以分类讨论, \(s=t\) 时是平凡情况, 注意这里到达后这个点会变成已到达的, 如果要访问同一层的其他点就会回不来, 于是这里直接等于 \(f^{[2]}_i+1\). 剩下的情况一个是 \(s<t\), 一个是 \(s>t\).
\(s<t\) 的时候可以访问到 \(t\) 左侧的所有点, \(s>t\) 的时候可以访问到 \(t\) 右侧的所有点, 显然这个贡献和 \(s\) 无关, 正反扫一遍记录一下前后缀最大的 \(f^{[2]}_i\) 就好了.
以上贡献的时候都需要维护一个 pair
, 第一关键字是DP值, 第二关键字是来源. 用 pair
可以节省额外的记录方案代码. 注意 \(f^{[1]}_i\) 的来源记录的是同层的 \(s\) 的位置, \(f^{[2]}_i\) 的来源记录的是上一层的 \(t\) 的位置.
输出方案的时候处理一下同一层的特判一下 \(s=t\) 的情况第一个问题就做完了.
第二个问题依然比较恶心
下界最小流的模型很显然就不说了. 关键在于把这个图建出来. 因为它问的是所有可能出现在最优解的直线.
如果 \(u\) 沿↖↑↗三个方向可以到达 \(v\), 那么只要原点到 \(u\) 的最长路和 \(v\) 出发的最长路之和与第一问的答案相等就可以出现在最优解上. 那么就需要计算出从某个点出发的最长路的长度.
这个DP和第一问类似, 不再赘述. 不过这次可以在任意位置结束, 所以没有后继的时候贡献是 \(0\) 而不是 \(-\infty\).
最后注意一下不要写假Dinic...这题数据范围比较大, 假复杂度的Dinic很容易被卡...我猜Po姐就是因为写了假Dinic才没有阿克Day2的然而神仙Po姐实际上写了个费用流
参考代码
一杯茶, 一包烟, 一个破题调一天
#include <bits/stdc++.h>
const int MAXV=50010;
const int MAXN=50010;
const int MAXE=1e7+10;
const int INF=0x7FFFFFFF;
typedef std::pair<int,int> Pair;
struct Edge{
int from;
int to;
int flow;
bool blk;
Edge* rev;
Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* cur[MAXV];
Edge* top=E;
int n;
int ycnt;
int s[MAXN];
Pair a[MAXN];
int id[MAXN];
int bk1[MAXN];
int bk2[MAXN];
Pair fw1[MAXN];
Pair fw2[MAXN];
int depth[MAXV];
std::map<int,int> bkX;
std::map<int,int> bkN; // x+y fixed
std::map<int,int> bkZ; // x-y fixed
std::map<int,Pair> fwX;
std::map<int,Pair> fwN; // x+y fixed
std::map<int,Pair> fwZ; // x-y fixed
std::vector<Pair> pos[MAXN];
std::pair<Pair,int> P[MAXN];
int ReadInt();
int GetB(int);
Pair GetF(int);
bool BFS(int,int);
int Dinic(int,int);
int DFS(int,int,int);
void UpdateF(int,int);
void UpdateB(int,int);
Edge* Insert(int,int,int);
int main(){
n=ReadInt();
for(int i=1;i<=n;i++){
a[i].first=P[i].first.first=ReadInt();
s[i]=a[i].second=P[i].first.second=ReadInt();
P[i].second=i;
}
std::sort(s+1,s+n+1);
std::sort(P+1,P+n+1);
ycnt=std::unique(s+1,s+n+1)-(s+1);
for(int i=1;i<=n;i++)
pos[std::lower_bound(s+1,s+ycnt+1,P[i].first.second)-s].emplace_back(P[i].first.first,P[i].second);
pos[0].emplace_back(0,0);
UpdateF(0,0);
Pair ans;
for(int i=1;i<=ycnt;i++){
for(size_t j=0;j<pos[i].size();j++){
int x=pos[i][j].second;
auto p=GetF(x);
fw2[x]=p;
fw1[x]=Pair(p.first+1,x);
}
auto maxf=Pair(INT_MIN,0);
for(size_t j=0;j<pos[i].size();j++){
int p=pos[i][j].second;
id[p]=j;
fw1[p]=std::max(fw1[p],Pair(maxf.first+j+1,maxf.second));
if(fw2[p].first>=0)
maxf=std::max(maxf,Pair(fw2[p].first,p));
}
maxf=Pair(INT_MIN,0);
for(size_t j=pos[i].size()-1;j<pos[i].size();j--){
int p=pos[i][j].second;
fw1[p]=std::max(fw1[p],Pair(maxf.first+(pos[i].size()-j),maxf.second));
if(fw2[p].first>=0)
maxf=std::max(maxf,Pair(fw2[p].first,p));
UpdateF(p,fw1[p].first);
ans=std::max(ans,Pair(fw1[p].first,p));
}
}
int bkans=0;
for(int i=ycnt;i>=1;i--){
for(size_t j=0;j<pos[i].size();j++){
int x=pos[i][j].second;
bk2[x]=GetB(x);
bk1[x]=bk2[x]+1;
}
int maxb=0;
for(size_t j=0;j<pos[i].size();j++){
int p=pos[i][j].second;
bk1[p]=std::max(bk1[p],maxb);
maxb=std::max(maxb,int(bk2[p]+(pos[i].size()-j)));
}
maxb=0;
for(size_t j=pos[i].size()-1;j<pos[i].size();j--){
int p=pos[i][j].second;
bk1[p]=std::max(bk1[p],maxb);
maxb=std::max(maxb,int(bk2[p]+j+1));
UpdateB(p,bk1[p]);
bkans=std::max(bkans,bk1[p]);
}
}
printf("%d\n",ans.first);
std::vector<int> sol;
for(int cur=ans.second;cur!=0;){
sol.push_back(cur);
int i=std::lower_bound(s+1,s+ycnt+1,a[cur].second)-s;
if(fw1[cur].second!=cur){
int next=fw1[cur].second;
if(id[next]<id[cur]){
for(int j=id[cur]-1;j>id[next];j--)
sol.push_back(pos[i][j].second);
for(int j=0;j<=id[next];j++)
sol.push_back(pos[i][j].second);
}
else{
for(int j=id[cur]+1;j<id[next];j++)
sol.push_back(pos[i][j].second);
for(int j=pos[i].size()-1;j>=id[next];j--)
sol.push_back(pos[i][j].second);
}
cur=fw1[cur].second;
}
cur=fw2[cur].second;
}
for(int i=ans.first-1;i>=0;i--)
printf("%d%c",sol[i]," \n"[i==0]);
bkX.clear();
bkN.clear();
bkZ.clear();
int ss=n+1,tt=n+2,s=n+3,t=n+4;
Edge* Ex;
std::vector<Edge*> aux;
aux.push_back(Ex=Insert(t,s,INF));
for(int i=0;i<=n;i++){
Insert(s,i,INF);
Insert(i,t,INF);
}
auto check=[=,&aux](int r,int k){
if(fw1[r].first+bk1[k]==ans.first){
Insert(r,k,INF);
aux.push_back(Insert(ss,k,1));
aux.push_back(Insert(r,tt,1));
}
};
for(int i=ycnt;i>=0;i--){
for(auto p:pos[i]){
int x,y,r=p.second;
std::tie(x,y)=a[r];
if(bkX.count(x))
check(r,bkX[x]);
if(bkN.count(x+y))
check(r,bkN[x+y]);
if(bkZ.count(x-y))
check(r,bkZ[x-y]);
bkX[x]=r;
bkN[x+y]=r;
bkZ[x-y]=r;
}
}
Dinic(ss,tt);
for(auto p:aux)
p->blk=p->rev->blk=true;
int flow=Ex->rev->flow;
flow-=Dinic(t,s);
printf("%d\n",flow);
return 0;
}
int GetB(int i){
int ans=0;
int x=a[i].first;
int y=a[i].second;
if(bkX.count(x))
ans=std::max(ans,bkX[x]);
if(bkN.count(x+y))
ans=std::max(ans,bkN[x+y]);
if(bkZ.count(x-y))
ans=std::max(ans,bkZ[x-y]);
return ans;
}
void UpdateB(int i,int d){
if(d<0)
return;
int x=a[i].first;
int y=a[i].second;
auto f=[](int& x,int d){x=std::max(x,d);};
f(bkX[x],d);
f(bkN[x+y],d);
f(bkZ[x-y],d);
}
Pair GetF(int i){
int x=a[i].first;
int y=a[i].second;
Pair ans(INT_MIN,0);
if(fwX.count(x))
ans=std::max(ans,fwX[x]);
if(fwN.count(x+y))
ans=std::max(ans,fwN[x+y]);
if(fwZ.count(x-y))
ans=std::max(ans,fwZ[x-y]);
return ans;
}
void UpdateF(int i,int d){
if(d<0)
return;
int x=a[i].first;
int y=a[i].second;
auto p=Pair(d,i);
auto f=[](Pair& x,Pair d){x=std::max(x,d);};
f(fwX[x],p);
f(fwN[x+y],p);
f(fwZ[x-y],p);
}
int Dinic(int s,int t){
int ans=0;
while(BFS(s,t))
ans+=DFS(s,INF,t);
return ans;
}
bool BFS(int s,int t){
memset(depth,0,sizeof(depth));
std::queue<int> q;
cur[s]=head[s];
depth[s]=1;
q.push(s);
while(!q.empty()){
s=q.front();
q.pop();
for(Edge* i=head[s];i!=NULL;i=i->next){
if(!i->blk&&i->flow>0&&depth[i->to]==0){
depth[i->to]=depth[s]+1;
cur[i->to]=head[i->to];
if(i->to==t)
return true;
q.push(i->to);
}
}
}
return false;
}
int DFS(int s,int flow,int t){
if(s==t||flow<=0)
return flow;
int rest=flow;
for(Edge*& i=cur[s];i!=NULL;i=i->next){
if(!i->blk&&i->flow>0&&depth[i->to]==depth[s]+1){
int tmp=DFS(i->to,std::min(rest,i->flow),t);
if(tmp<=0)
depth[i->to]=0;
rest-=tmp;
i->flow-=tmp;
i->rev->flow+=tmp;
if(rest<=0)
break;
}
}
return flow-rest;
}
inline Edge* Insert(int from,int to,int flow){
Edge* ret=top;
top->from=from;
top->to=to;
top->flow=flow;
top->blk=false;
top->rev=top+1;
top->next=head[from];
head[from]=top++;
top->from=to;
top->to=from;
top->flow=0;
top->blk=false;
top->rev=top-1;
top->next=head[to];
head[to]=top++;
return ret;
}
inline int ReadInt(){
int x=0;
int sgn=1;
register char ch=getchar();
while(!isdigit(ch)){
sgn=(ch=='-'?-sgn:sgn);
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-'0';
ch=getchar();
}
return x*sgn;
}
[LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机的更多相关文章
- [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会
[LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会 题意 给定一个长度为 \(n\) 的字符串 \(s\), 对于所有 \(r\in[1,n]\) 求出 \(s\ ...
- [LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程
[LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程 题意 给定一张无向图, 每条边有一个距离和一个高度. 再给定 \(q\) 组可能在线的询问, 每组询问给定一个点 ...
- [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分
[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...
- [LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士
[LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士 题意 题面好啰嗦啊直接粘LOJ题面好了 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照 ...
- [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机
[UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...
- [BZOJ]4200: [Noi2015]小园丁与老司机
Time Limit: 20 Sec Memory Limit: 512 MBSec Special Judge Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维 ...
- LOJ#2134 小园丁与老司机
我的妈呀,这码农神题...... 第一问是个DP,要记录方案.先把纵向的转移建图.发现可以按照y坐标来划分阶段,每一层vector存一下,用前后缀最大值来转移. 第二问考虑所有可能成为最优方案的边.从 ...
- UOJ#132&bzoj4200[Noi2015]小园丁与老司机
看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...
- *LOJ#2134. 「NOI2015」小园丁与老司机
$n \leq 5e4$个平面上的点,从原点出发,能从当前点向左.右.上.左上或右上到达该方向最近的给定点.问三个问:一.最多经过多少点:二.前一问的方案:三.其所有方案种非左右走的边至少要开几辆挖掘 ...
随机推荐
- [题解向] CF#Global Round 1の题解(A $\to$ G)
这里是总链接\(Link\). \(A\) 题意:求\(\sum_{i=1}^{k} a_i\times b^{k-i}\)的奇偶性, \(k = \Theta(n \log n)\) --其实很容易 ...
- The trap of Bash trap
Can you spot the problem with the following Bash script? resource_created="false" function ...
- Java并发编程杂记(2)
对象共享 synchronized 设定原子性确定临界区 + 内存可见性 要解决如下问题 防止一个线程在使用对象状态而另一个线程在修改对象状态:且当一个线程修改了对象状态后,对其他线程可见. 可见 ...
- java基础第十五篇之IO流和递归算法
FileInputStream : 输入流 int available() : 一次读取所有的字节数 read() : 将文件上的字节读取到内存的数组中 FileOutputStream : 输出流 ...
- MySQL基于报错注入1
0x1 判断注入点: http://www.xxxx.ro/s.php?id=1' 那么尝试闭合下单引号 http://www.xxxx.ro/s.php?id=1' --+ 0x2 枚举下表的列 h ...
- -force_load (加载静态库崩溃)
-force_load Crash Log: Last Exception Backtrace: 0 CoreFoundation 0x2f087f06 __e ...
- switch...case...语句分析(大表跟小表何时产生)
一.switch...case...的格式 switch(表达式) { case 常量表达式1: 语句; break; case 常量表达式2: 语句; break; case 常量表达式3: 语句; ...
- vscode使用formate格式化less遇到的坑
就是这个家伙 我的代码 @input-padding-y : 8px;@input-padding-x : 12px; @input-padding-y-lg : @input-padding-y + ...
- 【mysql】pymysql.err.InterfaceError Interface Error: (0, '')
八成是丢失连接了 while 1: try: self.conn.ping(reconnect=True) self.cur.execute(sql,tuple(item.values())) sel ...
- .net core使用百度webupload上传图片
后端代码: /// <summary> /// 图片上传,上传路径: "/Uploads/Images/" + folder + "/" + sav ...