noname
【问题描述】
给定一个长度为n的正整数序列,你的任务就是求出至少需要修改序列中的多少个数才能使得该数列成为一个严格(即不允许相等)单调递增的正整数序列,对序列中的任意一个数,你都可以将其修改为任意的正整数。
【输入格式】
每个测试点第一行为一个正整数T,表示该测试点内的数据组数,你需要对该测试点内的T组数据都分别给出正确的答案才能获得该测试点的分数。
接下来T组数据,每组数据第一行一个正整数n,表示序列长度,接下来一行n个正整数描述这个序列。
【输出格式】
对于每一组测试数据输出一行一个非负整数表示答案。
【输入输出样例】
noname.in | noname.out |
4 3 1 1 2 3 2 2 3 4 1 2 3 4 4 2 3 3 4 | 2 1 0 2 |
【样例解释】
第一组数据中,由于正整数的限制,只能修改序列中第2、3个数。
第二组数据中,将第一个数修改为1即可。
第三组数据已经符合条件,不需要修改。
第四组数据中,由于正整数的限制,只修改一个数并不能完成任务,而需要同时修改前两个数或者后两个数。
【数据规模与约定】
对于30%的数据,有1≤n≤10。
对于80%的数据,有1≤n,T≤50,且输入序列中每个数均不超过50。
对于100%的数据,有1≤n≤50000,1≤T≤5000,输入序列中每个数均为不超过1000000000(10^9)的正整数,且每个测试点中T组数据对应的n值总和不超过500000。
【题解思路】
这题看着怎么这么像LIS啊?(:雾
回顾一下dp入门题LIS ?
依稀记得这题是可以二分优化的吧:O(n^2) à O(nlogn)
我们思考类型题:
- 把一个序列改成非严格单调递增的(单调不下降的),至少需要修改多少个数?
A:用该序列总长度减去最长不下降子序列的长度。
2.那么把一个序列改成严格单增的呢?
A:用该序列总长度减去最长上升子序列的长度?标准错答案啊。
正解是:构造序列B[i] = A[i] – i;答案即为序列总长度减去B的最长不下降子序列。
Why??? 因为要求严格单调递增,所以前一项与后一项至少相差一,将这个必要的差值减去,就相当于求B改成非严格单增最少需修改的数,也就相当于第一个问题。
3.(本题)在2的条件下把整数改成了正整数。题目意思就变成:求首项为非负整数的最长上升子序列。
那岂不是很简单的一个dp ???当然还是需要二分优化的啦。
【code】
#include<bits/stdc++.h>
using namespace std;
const int maxx= ;
int a[maxx],b[maxx],n,T,f[maxx],len;
inline int read(){
int x=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
int find(int x){
int l = ,r = len + ;
if(x < f[])return ;
while(l+ < r){
int m = l + r>>;
if(f[m] <= x) l=m;
else r = m;
}
return l;
}
void work(){
len = ;
for(int i = ;i <= n;i++){
if(b[i] < )continue;
int id = find(b[i]);
len = max(id + ,len);
f[id+] = min(f[id+],b[i]);
}
printf("%d\n",n-len);
}
int main(){
freopen("noname.in","r",stdin);
freopen("noname.out","w",stdout);
T = read();
while(T--){
n = read();
memset(f,0x3f,sizeof(f));
for(int i = ;i <= n; ++i){
a[i] = read();
b[i] = a[i] - i;
}
work();
}
return ;
}