Bzoj4361:isn:树状数组+动态规划+容斥

22 篇文章 0 订阅
3 篇文章 0 订阅

题目链接:isn

上来就觉得这不是个DP就是个数学题,结果他既是DP又是数学题QAQ

设f[i][j]表示以i结尾的长度为j的下降序列,则f[i][j]的计算式为

发现暴力计算这个是的,于是我们借助树状数组求逆序对的方法计算f[i][j],这样就优化到了

我们设g[i]表示长度为i的非下降子序列个数,则

最后统计答案时还要用到容斥原理。单纯考虑i贡献的答案是ans[i]=

但是可以发现我们枚举剩下(n-i)个数的删除顺序时可能已经在某个时刻形成了非降序列,按题意这时候应该停止但是我们并没有停止

在考虑我们如果枚举到了一个非法的删除序列,他一定是从i+1个数删除一个来的,我们枚举删的是哪个,减去这时候的序列个数

因为不管枚举到的这个序列合法非法,他一定被计算到了ans[i]中,所以这么做是对的

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define LL long long
using namespace std;
const LL mod=1000000007;
const int maxn=2010;
int n,N,pos[maxn];
LL a[maxn],t[maxn],sum[maxn][maxn];
LL f[maxn][maxn],g[maxn],fac[maxn];

void init_hash(){
	for (int i=1;i<=n;++i) t[i]=a[i];
	sort(t+1,t+n+1);
	N=unique(t+1,t+n+1)-t-1;
}

int lowbit(int x){return x&-x;}

void update(int id,int x,LL v){
	for (int i=x;i<=n;i+=lowbit(i))
	    sum[id][i]=(sum[id][i]+v)%mod;
}

LL query(int id,int x){
	LL ret=0;
	for (int i=x;i;i-=lowbit(i))
		ret=(ret+sum[id][i])%mod;
	return ret;
}

int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
	init_hash(); fac[1]=1;
	for (int i=1;i<=n;++i)
	    pos[i]=lower_bound(t+1,t+N+1,a[i])-t;
	for (int i=2;i<=n;++i) fac[i]=1LL*fac[i-1]*i%mod;
	update(0,1,1);
	for (int i=1;i<=n;++i)
	    for (int j=i;j>=1;--j){
			f[i][j]=(f[i][j]+query(j-1,pos[i])%mod)%mod;
			update(j,pos[i],f[i][j]%mod);
	    }
	for (int i=1;i<=n;++i)
	    for (int j=i;j<=n;++j)
			g[i]=(g[i]+f[j][i])%mod;
	LL ans=0;
	for (int i=1;i<=n;++i)
	    ans=((ans%mod+(fac[n-i]%mod*g[i]%mod)%mod)%mod-fac[n-i-1]%mod*g[i+1]%mod*(i+1)%mod+mod)%mod;
	printf("%lld",ans);
}


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值