http://www.lydsy.com/JudgeOnline/problem.php?id=1049

题意:给一个长度为n的整数序列。把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。1. 询问最少需要改变多少个数。 2. 在1的条件下每个数改变的绝对值之和的最小值。(n<=35000, 数据随机)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define error(x) (!(x)?puts("error"):0)
#define rdm(x, i) for(int i=ihead[x]; i; i=e[i].next)
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; } const int N=50005, oo=~0u>>2;
int a[N], n, b[N], g[N], pos[N], nxt[N], inext[N], f[N]; void init() {
for1(i, 1, n) g[i]=oo;
for1(i, 1, n) {
int k=upper_bound(g+1, g+1+i, b[i])-g;
f[i]=k;
g[k]=b[i];
nxt[i]=pos[k];
inext[i]=pos[k-1];
pos[k]=i;
}
}
ll ans[N], c[N];
void work() {
for1(i, 2, n) {
int p=inext[i], pos=1;
ans[i]=oo;
while(p) { if(b[i]>=b[p]) pos=p; p=nxt[p]; }
p=pos;
ll sum=0, mx=-oo; c[p]=0;
for1(j, p+1, i-1) c[j]=c[j-1]+(b[j]<b[i]?1:-1);
for3(j, i-1, p) {
if(b[j]<=b[i] && f[j]+1==f[i]) {
ans[i]=min(ans[i], ans[j]+sum);
ans[i]=min(ans[i], ans[j]+sum-(ll)(b[i]-b[j])*(mx-c[j]));
}
if(mx<c[j]) mx=c[j];
sum+=abs(b[i]-b[j]);
}
}
} int main() {
read(n); b[1]=-oo; ++n;
for1(i, 2, n) read(a[i]), b[i]=a[i]-i;
++n; b[n]=oo-n;
init();
work();
printf("%d\n%lld\n", n-f[n], ans[n]);
return 0;
}

  

又是一题神题啊。orz

首先第一个问很容易看出

f[i]=min{f[j]+1, a[i]-a[j]>=i-j}

设b[i]=a[i]-i

f[i]=min{f[j]+1, b[i]>=b[j]}

然后就是lis的log算法。。。。

第二个问,好神!!!

首先发现,如果有b[i]>=b[j]且f[i]==f[j]+1时,区间[j, i]中的点一定都是大于b[i]或者小于b[j],很显然吧。。

而我们要将[j, i]的点变成合法序列一定是存在一个点t,使得[j, t]变成b[j],[t+1, i]变成b[i]。(在原序列中就变成了a[j], a[j+1]=a[j]+1, a[j+2]=a[j]+2...这样)

如何证明?不会QAQ

试着证明一下:考虑最优点t,假设b[t]不变成b[j],而是变成b'[t]>b[j],且b'[t]<b[i]。那么因为原b[t]<b[j]或者b[t]>b[i],显然费用为b'[t]-b[t]>b[j]-b[t](当b[t]<b[j]时)b[t]-b'[t]>b[t]-b[i](当b[t]>b[i]时),得出b[j]<b'[t]<b[i]没有b'[t]=b[j]或=b[i]优,即证。

那么这样搞是n^3的,,,,,,,,,,,,,,

先试着搞成n^2。考虑当前转移点为i

我们首先找出离i最远的j,b[i]>=b[j]且f[i]==f[j]+1,那么所有的转移点都包含在区间[j, i]中。

考虑从i向左枚举至j,当前在k,此时假设现在将所有[k+1, i-1]的点全部变为b[i],那么当k是转移点时,我们需要得到最小值。

因为现在[k+1, i-1]全都是变成了b[i],那么假设要将其中的点变成b[j],显然:如果原b[x]>b[i],那么费用还需要+(b[i]-b[j]),如果原b[x]<b[i],那么费用就需要-(b[i]-b[j])。假设[k, i]中最优点t,[k+1, t]有y个比b[i]大的点,z个比b[i]小的点,那么需要变化的费用为:

sum-(b[i]-b[k])*z+(b[i]-b[k])*y=sum-(b[i]-b[k])*(z-y),而区间[j, i]中所有转移点k显然是b[k]单调不降的,所以b[i]-b[k]在单调不增的,所以目标变成最大化(z-y)。

所以考虑前缀和找出最大的差就行了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

那么问题变成n^2...

然后题目说。。。随机数据。。。。。。。。。。。。水过。

(还有注意一点是,如何快速找到j点,那么我们在lis时向所有转移点连边,然后快速找到即可,否则复杂度会更大)

最新文章

  1. 2 Unique Binary Search Trees II_Leetcode
  2. 将Matlab中的矩阵输出到txt文件
  3. 这是BUG吗?
  4. [TypeScript] Understanding Decorators
  5. c\c++复习基础要点08--c++单例模式
  6. [置顶] Android框架攻击之Fragment注入
  7. mac 搭建node 开发环境记录
  8. 小甲鱼OD学习第12讲
  9. golang slice分割和append copy还是引用
  10. Triangle Count
  11. 【原创】Git 分支的合并【Learn Git Branching】
  12. pytorch visdom可视化工具学习—1—安装和使用
  13. iOS Xcode 10: Multiple commands produce
  14. 【翻译】Apache Shiro10分钟教程
  15. python自学第13天 hashlib,re模块
  16. 如何在Lua与C/C++之间实现table数据的交换
  17. oracle使用随笔
  18. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例
  19. luogu P3391 【模板】文艺平衡树(Splay)
  20. CSS+JS笔记

热门文章

  1. 利用cocostudio库函数 实现左右滑动的背包栏UI (cocos2d-x 2.2.0)
  2. sqltext的参数化处理
  3. MinGW/MSYS 交叉编译环境搭建
  4. DP:Sumsets(POJ 2229)
  5. 手把手教你学会 Emacs 定制
  6. 【python】一个简单的贪婪爬虫
  7. CodeForces - 427A (警察和罪犯 思维题)
  8. SimpleDataFormat格式
  9. 分类and分类延展
  10. Android实现圆形圆角图片