P3970 [TJOI2014] 上升子序列
这题在省选算是简单的吧。
看了一圈题解,好像大家都去重了,写一个不用去重的抽象做法。
先离散化,设 表示以 这个数为结尾的子序列最多有多少个。
然后直接枚举 的数转移即可,发现可以线段树优化到单次 。
但是题目要求不重复。
一个重要的观察:对于两个相同的数 和 ,其中 ,以 这个位置为结尾的子序列一定是要不少于以 这个位置为结尾的子序列个数。
原因显然,因为以 这个位置为的所有子序列去掉 ,完全可以换上 代替,相当于 的答案覆盖了 ,所以是不少于。
既然 可以覆盖到 的答案,由此可以得出一个结论,就是对于 这个数,它一定覆盖了前面等于他的所有数的答案。对于 后面的数,我们只用关心 即可。
那就解决了重复的问题了,对于当前的数,我们只用统计当前前面出现过且小于的数的答案即可,并且更新当前数对应的 值。可以直接更新的原因是具有单调性。
注意初始化要为 ,就当长度为 的也算进去,为了后面统计的时候有值。最后再减去即可。
警钟敲烂:在取模的情况下不能用取最大值或最小值,这都是不准的。
#include<bits/stdc++.h>
using namespace std;
const int N =1e6+10;
const int mod=1e9+7;
#define int long long
struct node{
int val,id;
}a[N];
int dp[N],lst[N],c[N],d[N<<1];
bool cmp(node a,node b){
return a.val<b.val;
}
int Query(int l,int r,int s,int t,int p){
if(l<=s&&t<=r) return d[p];
int mid=(s+t)>>1,sum=0;
if(l<=mid) sum+=Query(l,r,s,mid,p<<1),sum%=mod;
if(r>mid) sum+=Query(l,r,mid+1,t,p<<1|1),sum%=mod;
return sum;
}
void update(int l,int r,int change,int s,int t,int p){
if(l<=s&&t<=r){
d[p]+=change,d[p]%=mod;
return ;
}
int mid=(s+t)>>1;
if(l<=mid) update(l,r,change,s,mid,p<<1);
if(r>mid) update(l,r,change,mid+1,t,p<<1|1);
d[p]=(d[p<<1]+d[p<<1|1])%mod;
return ;
}
signed main()
{
// freopen("1.in","r",stdin);
// freopen("ans1.out","w",stdout);
int n,node=1;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].val,a[i].id=i;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
if(i==1) node++;
else if(a[i].val!=a[i-1].val) node++;
c[a[i].id]=node;
}
int ans=0;
for(int i=1;i<=n;i++){
int now=Query(1,c[i]-1,1,n,1);
update(c[i],c[i],now+1-dp[c[i]],1,n,1),dp[c[i]]=now+1,dp[c[i]]%=mod;
}
for(int i=2;i<=node;i++)
ans+=dp[i]-1,ans%=mod;
cout<<ans<<endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通