「NOI2015」小园丁与老司机

要不是这道码农题,去年就补完了NOI2015,其实两问都比较simple,但是写起来很恶心。

先解决第一问,记 \(dp[i]\) 表示老司机到达第 \(i\) 棵树时能许愿的最多的树的数量,考虑没有左右方向的决策时,转移是个DAG,直接做。 对于左右方向的决策,会发现最优解一定是以下两种中的一种。

  1. 对于点 \(x\) ,从左边的某个点 \(y\) 进入这一行,然后先向左走走到最左边的一棵树,然后再调头走到点 \(x\) 。
  2. 对于点 \(x\) ,从右边的某个点 \(y\) 进入这一行,然后先向右走走到最右边的一棵树,然后再调头走到点 \(x\)。

那么就将 \(y\) 坐标相同的点放在一起转移,维护一下前缀和后缀 \(\max\) 即可。

第一个比较恶心的点在于输出方案,单纯记录每个点是由谁转移的话会发现有环,需要记录一下每个点由左右转移过来的方案和由其它转移过来的方案,当发现某一步是由左右转移过来的时候,下一步钦点不能由左右转移过来即可。

考虑第二问,没有了左右的转移边,相当于将所有其它的且在某个最优解上的转移构成的DAG取出来,求这个DAG的最小链覆盖,直接跑有源汇上下界最小流即可。

这里有第二个比较恶心的点,求一个转移是否在最优解上非常麻烦,需要求一个 \(dp'[i]\) 表示老司机从 \(i\) 出发能许愿的最多的树的数量,还是左右方向的决策,这次变成了

  1. 对于点 \(x\) ,先往左走一直走到底,再往右走走到其右边的某一棵树 \(y\) 往上走。

  2. 对于点 \(x\) ,先往右走一周走到底,再往左走走到其左边的某一棵树 \(y\) 往上走。

把式子拆开来发现还是可以前缀和以及后缀和维护的,这个地方比较容易晕,想我这种比较菜的人就要多想想。

code

/*program by mangoyang*/
#include <bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define fi first
#define se second
const int N = 1000005, M = 2000005;
vector<int> ed[N], fx[N];
vector<pair<int, int> > v1[N], v2[N], v3[N], v4[N];
int s[N], ax[N], ay[N], fuck[N], n, len;
inline int D(int x){ return lower_bound(s + 1, s + len + 1, x) - s; }
namespace flow{
queue<int> q;
int a[M], cap[M], nxt[M], head[N], cur[N], dis[N], S, T, cnt = 1;
inline void addedge(int x, int y, int z){
a[++cnt] = y, cap[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;
a[++cnt] = x, cap[cnt] = 0, nxt[cnt] = head[y], head[y] = cnt;
}
inline int bfs(){
memset(dis, -1, sizeof(dis)), dis[S] = 0, q.push(S);
for(; !q.empty(); q.pop()){
int u = q.front();
for(int p = head[u]; p; p = nxt[p]){
int v = a[p];
if(~dis[v] || !cap[p]) continue;
dis[v] = dis[u] + 1, q.push(v);
}
}
return ~dis[T];
}
inline int dfs(int u, int flow){
if(u == T || !flow) return flow;
int used = 0;
for(int &p = cur[u]; p; p = nxt[p]){
int v = a[p];
if(dis[v] != dis[u] + 1 || !cap[p]) continue;
int x = dfs(v, min(flow, cap[p]));
used += x, flow -= x, cap[p] -= x, cap[p^1] += x;
if(!flow) break;
}
return used;
}
inline void setflow(int x, int y){ S = x, T = y; }
inline int getflow(){
int res = 0;
for(; bfs(); res += dfs(S, inf))
for(int i = 1; i <= n + 4; i++) cur[i] = head[i];
return res;
}
}
namespace task2{
int NS, NT, S, T;
inline void init(){
NS = n + 1, NT = n + 2, S = n + 3, T = n + 4;
}
inline void add(int x, int y, int a, int b){
flow::addedge(NS, y, a);
flow::addedge(x, NT, a);
flow::addedge(x, y, b - a);
}
inline void solve(){
for(int i = 1; i <= n; i++){
add(S, i, 0, inf);
add(i, T, 0, inf);
}
flow::setflow(NS, NT);
flow::getflow();
flow::addedge(T, S, inf);
cout << flow::getflow() << endl;
} }
namespace task1{
int f[N], ff[N], st[N], g[N], id[N], gg[N], pr[N], spec[N], ans, top;
inline void chkmax(int &x, int y){ if(y > x) x = y; }
inline bool cmp(int x, int y){
return ay[x] == ay[y] ? ax[x] > ax[y] : ay[x] < ay[y];
}
inline void trans(int x){
for(int i = 0; i < (int) ed[x].size(); i++)
if(!spec[ed[x][i]] && f[x] + 1 > f[ed[x][i]])
f[ed[x][i]] = f[x] + 1, pr[ed[x][i]] = x;
}
inline void trans2(int x){
for(int i = 0; i < (int) fx[x].size(); i++)
if(!spec[fx[x][i]]) chkmax(ff[fx[x][i]], ff[x] + 1);
}
inline void solve(){
for(int i = 1; i <= n; i++) id[i] = i;
for(int i = 1; i <= n; i++) f[i] = g[i] = -inf, ff[i] = 1;
f[n] = ff[n] = 0;
sort(id + 1, id + n + 1, cmp);
for(int i = 1, last; i <= n; i = last + 1){
last = i;
while(last < n && ay[id[last+1]] == ay[id[i]]) last++;
for(int j = i; j <= last; j++) spec[id[j]] = 1;
int Left = -inf, Right = -inf, posL = 0, posR = 0;
for(int j = i; j <= last; j++){
if(Left != -inf) g[id[j]] = Left + j - i, gg[id[j]] = posL;
if(f[id[j]] > Left) Left = f[id[j]], posL = id[j];
}
for(int j = last; j >= i; j--){
if(Right != -inf && Right + last - j > g[id[j]])
g[id[j]] = Right + last - j, gg[id[j]] = posR;
if(f[id[j]] > Right) Right = f[id[j]], posR = id[j];
}
for(int j = i; j <= last; j++){
if(g[id[j]] > f[id[j]]) f[id[j]] = g[id[j]], fuck[id[j]] = 1;
if(f[id[j]] != -inf) trans(id[j]);
}
for(int j = i; j <= last; j++) spec[id[j]] = 0;
}
for(int i = 1; i <= n; i++) ans = max(ans, f[i]);
int pos = n;
for(int i = 1; i <= n; i++) if(f[i] == ans) pos = i;
while(pos != n){
if(fuck[pos]){
int l = lower_bound(v1[D(ay[pos])].begin(), v1[D(ay[pos])].end(), make_pair(ax[pos], pos)) - v1[D(ay[pos])].begin();
int r = lower_bound(v1[D(ay[pos])].begin(), v1[D(ay[pos])].end(), make_pair(ax[gg[pos]], gg[pos])) - v1[D(ay[pos])].begin();
if(l < r){
for(int i = l; i < r; i++) st[++top] = v1[D(ay[pos])][i].se;
for(int i = (int) v1[D(ay[pos])].size() - 1; i > r; i--) st[++top] = v1[D(ay[pos])][i].se;
}
else{
for(int i = l; i > r; i--) st[++top] = v1[D(ay[pos])][i].se;
for(int i = 0; i < r; i++) st[++top] = v1[D(ay[pos])][i].se;
}
pos = gg[pos], fuck[pos] = 0;
}
else st[++top] = pos, pos = pr[pos];
}
printf("%d\n", ans);
for(int i = top; i >= 1; i--) printf("%d ", st[i]);
puts("");
reverse(id + 1, id + n + 1);
for(int i = 1; i <= n; i++) g[i] = -inf;
for(int i = 1, last; i <= n; i = last + 1){
last = i;
while(last < n && ay[id[last+1]] == ay[id[i]]) last++;
for(int j = i; j <= last; j++) spec[id[j]] = 1;
int Left = -inf, Right = -inf;
for(int j = i; j <= last; j++){
g[id[j]] = Left + last;
chkmax(Left, ff[id[j]] - j);
}
for(int j = last; j >= i; j--){
g[id[j]] = Max(g[id[j]], Right - i);
chkmax(Right, ff[id[j]] + j);
}
for(int j = i; j <= last; j++){
ff[id[j]] = Max(ff[id[j]], g[id[j]]);
if(ff[id[j]] != -inf) trans2(id[j]);
}
for(int j = i; j <= last; j++) spec[id[j]] = 0;
}
for(int i = 1; i <= n; i++)
for(int j = 0; j < (int) ed[i].size(); j++){
int v = ed[i][j];
if(ay[v] == ay[i] || v == n) continue;
if(f[i] + ff[v] == ans)
task2::add(i, v, 1, inf);
} }
}
inline void linkpre(vector<pair<int, int> > &A, pair<int, int> x){
vector<pair<int, int> >::iterator it;
it = lower_bound(A.begin(), A.end(), x);
if(it != A.begin()) --it, ed[x.se].push_back(it->se);
}
inline void linknxt(vector<pair<int, int> > &A, pair<int, int> x){
vector<pair<int, int> >::iterator it;
it = upper_bound(A.begin(), A.end(), x);
if(it != A.end()) ed[x.se].push_back(it->se);
}
int main(){
read(n), s[++len] = 0, ++n;
for(int i = 1; i < n; i++){
read(ax[i]), read(ay[i]);
s[++len] = ax[i], s[++len] = ay[i];
s[++len] = ax[i] + ay[i], s[++len] = ax[i] - ay[i];
}
sort(s + 1, s + len + 1);
len = unique(s + 1, s + len + 1) - s - 1;
for(int i = 1; i <= n; i++){
v1[D(ay[i])].push_back(make_pair(ax[i], i));
v2[D(ax[i])].push_back(make_pair(ay[i], i));
v3[D(ax[i]-ay[i])].push_back(make_pair(ay[i], i));
v4[D(ax[i]+ay[i])].push_back(make_pair(ay[i], i));
}
for(int i = 1; i <= len; i++){
sort(v1[i].begin(), v1[i].end());
sort(v2[i].begin(), v2[i].end());
sort(v3[i].begin(), v3[i].end());
sort(v4[i].begin(), v4[i].end());
}
for(int i = 1; i <= n; i++){
linkpre(v1[D(ay[i])], make_pair(ax[i], i));
linknxt(v1[D(ay[i])], make_pair(ax[i], i));
linknxt(v2[D(ax[i])], make_pair(ay[i], i));
linknxt(v3[D(ax[i]-ay[i])], make_pair(ay[i], i));
linknxt(v4[D(ax[i]+ay[i])], make_pair(ay[i], i));
}
for(int i = 1; i <= n; i++)
for(int j = 0; j < (int) ed[i].size(); j++)
fx[ed[i][j]].push_back(i);
task2::init();
task1::solve();
task2::solve();
return 0;
}

最新文章

  1. 【代码笔记】iOS-获得当前硬盘空间
  2. 一、 Java的值传递和引用传递
  3. C++变量的左值和右值
  4. chm文件打开空白无内容的解决办法
  5. 51nod1125 交换机器的最小代价
  6. C# 微信支付证书使用
  7. 169. Majority Element
  8. 使用mysqldump 对数据库进行备份的乱码问题
  9. javascript提高篇+吐槽
  10. python os语法
  11. [poj2342]Anniversary party_树形dp
  12. js同步、异步、延时、无阻塞加载
  13. svcutil生成List类型不转换成数组
  14. python第四十三天--第三模块考核
  15. 预防 app crash 之 unrecognized selector
  16. JDK 的配置和反编译工具的使用---------------Java知识点
  17. extjs 分组函数自定义统计
  18. P4 Tutorials Flowlet Switching
  19. php动态链接扩展库
  20. ubuntu的boot分区报警,删除无用内核文件。

热门文章

  1. 一个很简单的SpringCloud项目,集成Feign、Hystrix
  2. Golang循环中调用go func参数异常分析
  3. ORACLE百分比分析函数RATIO_TO_REPORT() OVER()
  4. mysql查询表大小
  5. 国内加速访问 GitHub
  6. JavaScript/JQuery自执行函数
  7. Python实现感知器的逻辑电路(与门、与非门、或门、异或门)
  8. Java 获取对象全部属性 包括 父类属性
  9. USB驱动程序涉及的概念及框架
  10. unity texture贴图纹理