对于一个dp式子,考虑用数据结构来减小dp的时间复杂度
因为对于每一个奶牛,都是通过最右端-1~最左端-1转移而来
所以有式子
可以用一个线段树维护,每次查询区间最小值,然后用区间最小值进行区间dp
struct segment_tree{
long long tr[800010],tag[800080];
void pushdown(long long id,long long l,long long r){
if(l==r){
tr[id]=min(tr[id],tag[id]);
}else{
tr[id]=min(tr[id],tag[id]);
tag[id<<1]=min(tag[id],tag[id<<1]);
tag[id<<1|1]=min(tag[id],tag[id<<1|1]);
}
tag[id]=0x3f3f3f3f3f;
}
void add(long long id,long long l,long long r,long long x,long long w){
if(l==r){
tr[id]=w;
return ;
}
long long mid=(l+r)>>1;
if(mid>=x) add(id<<1,l,mid,x,w);
else add(id<<1|1,mid+1,r,x,w);
tr[id]=min(tr[id<<1],tr[id<<1|1]);
}
void insert(long long id,long long l,long long r,long long x,long long y,long long w){
pushdown(id,l,r);
if(l>=x&&r<=y){
tag[id]=w;
pushdown(id,l,r);
return ;
}
int mid=(l+r)>>1;
if(mid>=x) insert(id<<1,l,mid,x,y,w);
if(mid<y) insert(id<<1|1,mid+1,r,x,y,w);
tr[id]=min(tr[id<<1],tr[id<<1|1]);
}
long long query(long long id,long long l,long long r,long long x,long long y){
pushdown(id,l,r);
if(l>=x&&r<=y) return tr[id];
int mid=(l+r)>>1;
long long ans=0x3f3f3f3f3f;
if(mid>=x) ans=min(ans,query(id<<1,l,mid,x,y));
if(mid<y) ans=min(ans,query(id<<1|1,mid+1,r,x,y));
return ans;
}
};
令dp[i][j]是以第i位结尾的长度为j的子序列个数
对于每个a[i]<a[j],都有
树状数组维护所有小于a[i]的数的dp前缀和就好了
for(int j=1;j<=x;j++) dp[j][1]=1;
for(int i=2;i<=y;i++){
for(int j=1;j<=x;j++){
int o=mp[num[j]];
dp[j][i]=ty.query(o-1);
ty.insert(o,dp[j][i-1]);
}
for(int j=1;j<=x;j++){
int o=mp[num[j]];
ty.insert(o,-dp[j][i-1]);
}
}
long long ans=0;
for(int i=1;i<=x;i++){
ans=(ans+dp[i][y])%mod;
}
for(int i=2;i<=y;i++){
for(int j=1;j<=x;j++){
dp[j][i]=0;
}
}
还是上升子序列,先不考虑不包括1和重复,这有
dp[1~n]的初始值为1
然后考虑重复,每次遇到重复,用这次算出来的值剪掉上次的值就是中间一段的答案了
最后答案减掉所有数字个数,就是去掉是1的情况