2/9 P1020 最长不上升子序列 (3/16补档)(3/26补档)
放个链接先:https://www.luogu.com.cn/problem/P1020
做的时候(×)抄的时候(√)一直不懂为什么替换掉元素不会有影响,为什么len不用变,后面怎么改变len的值来着,真是太逊了呜呜
在deepseek的支持下得到了理解(以最长不上升子序列为例):
当一个数比当前的数大时,就把它替换到
dp [ upper_bound (dp+1 , dp+1+len , a[i] , greater<>( ) ) -dp ] greater<>()是为了把找到更大的变为找到更小的!
这时候末尾元素不变,len不变,我们要的就是这个效果!
因为我们不知道这个数 带来的新的可能性 会不会比当前的长,所以先放在那里
当我们不断往下找的时候,其他新的可能性也会不断加入到前面
最终有可能新的可能性追上了当前的长度len,这时候len同时记录新旧,下一个自然会过渡到更有优势的那一个可能性啦~
用DS的话来解释就是:
维护一个尽可能“有潜力”的序列(末尾元素尽量小/大),方便后续元素扩展
替换不影响最终长度:替换操作只优化后续扩展的可能性,不改变已计算的序列长度上限。
另外,upper_bound/lower_bound 是二分查找,会比较快!(其实就是不会自己写二分×)
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL n,h[100010],dp1[100010],dp2[100010],len1,len2;
int main()
{
while(scanf("%d",&h[++n])!=EOF);
n--,len1=1,len2=1;
dp1[1]=h[1],dp2[1]=h[1];
for(LL i=2;i<=n;i++)
{
if(h[i]<=dp1[len1])
{
dp1[++len1]=h[i];
}
else
{
LL re=upper_bound(dp1+1,dp1+1+len1,h[i],greater<LL>())-dp1;
dp1[re]=h[i];
}
if(h[i]>dp2[len2])
{
dp2[++len2]=h[i];
}
else
{
LL re=lower_bound(dp2+1,dp2+1+len2,h[i])-dp2;
dp2[re]=h[i];
}
}
cout<<len1<<endl<<len2;
return 0;
}
(3/16补档如下)
另一种方法:
使用树状数组
需要的两个基本函数:
查询前面最大值:
int qmax(int x)
{
int ans=0;
for(;x;x-=x&-x) ans=max(tr[x],ans);
return ans;
}
插入元素:
void add(int x,int y)
{
for(;x<=n;x+=x&-x) tr[x]=max(tr[x],y);
}
AC代码
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i],s[i]=a[i];
sort(s+1,s+1+n);
m=unique(s+1,s+1+n)-(s+1);
for(int i=1;i<=n;i++) a[i]=lower_bound(s+1,s+1+n,a[i])-s;
for(int i=1;i<=n;i++) f[i]=qmax(a[i])+1,add(a[i],f[i]);
memset(tr,0,sizeof(tr));
for(int i=n;i>=1;i--) g[i]=qmax(n-a[i]+1)+1,add(n-a[i]+1,g[i]);
memset(tr,0,sizeof(tr));
a[n+1]=m+1;
for(int i=k+2;i<=n+1;i++)
{
add(a[i-k-1],f[i-k-1]),ans=max(ans,k+g[i]+qmax(a[i]));
}
if(k>=n-1) ans=n;
cout<<ans;
原理:
用 s 记录排序后的原数据 a
将 a 变为排序后的位置的引索
(为方便说明,原数据的大小仍使用 a [ i ] 来说明)
之后以树状数组(以下简称 tree)为中转
每次先查询 a [ i ] 排序后的引索(以下简称 index )
sort(s+1,s+1+n);
a[i]=lower_bound(s+1,s+1+n,a[i])-s;
找到 tree [ index ] 在内(此时该还没有数据,实际上找的是之前的)往前最大的数 + 1 ,就得到了第 i 个数结尾的最长不下降子序列的长度,然后将这个长度放到 tree [ index ]
for(int i=1;i<=n;i++) f[i]=qmax(a[i])+1;add(a[i],f[i]);
解释如下:
我们是按照 原来的数据 的顺序一个一个进行查询的,也就是说,
每个数是依次被我们放进 tree 数组中的,
而我们放进去的位置 就是 数据按大小排序对应的位置 ,
那么,
当我们放进一个新的数之后,前面记录的就是比自己小的数,并且按照大小排好序,并且是前面已经放好的数,
这是不是就是最长不下降子序列要求的? 自己在内的前面,按大小排序?
这一就是把数字换成了字符串而已,没什么不同,字符串的比较就是题目要求的字典序比较
但是蒻蒟看了半天发现输出ans写成了a,看了半天呜呜呜
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e6+10;
LL d,t,m,len,num;
string a[N],dp[N],ans[N],s;
void read()
{
cin>>s;
for(int i=0;i<s.size();i++)
{
if(isupper(s[i]))
{
a[++num]=s[i];
}
else
{
a[num]+=s[i];
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
read();
dp[1]=a[1],ans[1]=a[1],len=1;
for(int i=2;i<=num;i++)
{
if(a[i]>dp[len])
{
dp[++len]=a[i];
ans[len]=ans[len-1]+a[i];
}
else
{
LL re=lower_bound(dp+1,dp+1+len,a[i])-dp;
dp[re]=a[i];
ans[re]=ans[re-1]+a[i];
}
}
cout<<ans[len];
return 0;
}

浙公网安备 33010602011771号