题面

传送门

思路

首先,本题目的核心元素是非降子序列,而显然这个题目中的子序列只和序列的长度、位置,以及互相之间的包含关系,这些东西相关

所以我们可以依据这些先“猜”(实际上是估测一个类似的)$dp$方程:

设$dp[i][j]$表示以$i$个位置结尾的,长度为$j$的非降子序列个数

转移:$dp[i][j]=\sum_{k=1}^{i-1}[a[k]<a[i]]dp[k][j-1]$

这个东西显然可以用树状数组求逆序对的套路,在$O(n^2logn)$的时间内求出来

然后,我们定义$g[i]$表示剩下掉$i$个数,得到一个非降子序列的方法数,方程显然:

$g[i]=\sum_{j=1}^{n}dp[j][i]*(i!)$

注意不要漏掉了阶乘,这个是表示组合方法的

但是这个东西是有问题的:在去掉$i$个数的过程中,有可能在去掉第$j(j<i)$个数的时候就已经达成非降子序列、不能继续操作了

那么我们就需要容斥一下

我们令$ans[i]$表示减去了这些不合法方案以后的$g[i]$

考虑一个$j>i$的数对$(j,i)$,如果在$j$处就已经达成的话,方案数等于$ans[j]$,然后我们要从$i$个里面选出来$j-i$个作为在完成以后还删掉了的

那么显然重复的方案数就是$ans[j] \ast C_{j}^{j-i}\ast(j-i)!$

对于所有的$j>i$,把$g[i]$减掉上面那个东西得到$ans[i]$,然后所有$ans[i]$的和就是答案了

Code

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cassert>
#define ll long long
#define MOD 1000000007
#define rank DEEP_DARK_FANTASY
using namespace std;
inline int read(){
    int re=0,flag=1;char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') flag=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re*flag;
}
ll qpow(ll a,ll b){
    ll re=1;
    while(b){
        if(b&1) re=re*a%MOD;
        a=a*a%MOD;b>>=1;
    }
    return re;
}
void add(ll &x,ll y){
    x+=y;
    if(x>=MOD)x-=MOD;
}
void dec(ll &x,ll y){
    x-=y;
    if(x<0) x+=MOD;
}
struct BIT{
    ll a[2010];
    BIT(){memset(a,0,sizeof(a));}
    int lowbit(int x){
        return x&(-x);
    }
    void update(int x,ll val){
        for(;x<=2000;x+=lowbit(x)) add(a[x],val);
    }
    ll sum(int x){
        ll re=0;
        for(;x>0;x-=lowbit(x)) add(re,a[x]);
        return re;
    }
}T[2010];
int n,a[2010],rank[2010];ll dp[2010][2010],ans[2010],tmp[2010];
ll f[2010],finv[2010];
inline bool cmp(int l,int r){
    return a[l]<a[r];
}
void init(){
    int i,len=2000;f[0]=f[1]=finv[0]=finv[1]=1;
    for(i=2;i<=len;i++) f[i]=f[i-1]*i%MOD;
    finv[len]=qpow(f[len],MOD-2);
    for(i=len;i>2;i--) finv[i-1]=finv[i]*i%MOD;
}
ll C(ll x,ll y){
    return f[x]*finv[y]%MOD*finv[x-y]%MOD;
}
int main(){
    n=read();int i,j,cnt=0;ll re=0;
    init();
    for(i=1;i<=n;i++) a[i]=read(),rank[i]=i;
    sort(rank+1,rank+n+1,cmp);
    for(i=1;i<=n;i++){
        cnt++;
        while(a[rank[i]]==a[rank[i+1]]) a[rank[i]]=cnt,i++;
        a[rank[i]]=cnt;
    }
    for(i=1;i<=n;i++){
        for(j=i;j>=2;j--){
            dp[i][j]=T[j-1].sum(a[i]);
            T[j].update(a[i],dp[i][j]);
        }
        dp[i][1]=1ll;
        T[1].update(a[i],1ll);
    }
    for(i=1;i<=n;i++)
        for(j=i;j<=n;j++)
            add(tmp[i],dp[j][i]*f[n-i]%MOD);
    for(i=n;i>=1;i--){
        ans[i]=tmp[i];
        for(j=i+1;j<=n;j++)
            dec(ans[i],ans[j]*C(j,j-i)%MOD*f[j-i]%MOD);
        add(re,ans[i]);
    }
    printf("%lld\n",re);
}

[bzoj4361] isn [树状数组+dp+容斥原理]的更多相关文章

  1. BZOJ4361 isn 树状数组、DP、容斥

    传送门 不考虑成为非降序列后停止的限制,那么答案显然是\(\sum\limits_{i=1}^N cnt_i \times (N-i)!\),其中\(cnt_i\)表示长度为\(i\)的非降序列数量 ...

  2. codeforces 597C (树状数组+DP)

    题目链接:http://codeforces.com/contest/597/problem/C 思路:dp[i][j]表示长度为i,以j结尾的上升子序列,则有dp[i][j]= ∑dp[i-1][k ...

  3. hdu 4622 Reincarnation trie树+树状数组/dp

    题意:给你一个字符串和m个询问,问你l,r这个区间内出现过多少字串. 连接:http://acm.hdu.edu.cn/showproblem.php?pid=4622 网上也有用后缀数组搞得. 思路 ...

  4. Codeforces 597C. Subsequences (树状数组+dp)

    题目链接:http://codeforces.com/contest/597/problem/C 给你n和数(1~n各不同),问你长为k+1的上升自序列有多少. dp[i][j] 表示末尾数字为i 长 ...

  5. HDU2227Find the nondecreasing subsequences(树状数组+DP)

    题目大意就是说帮你给出一个序列a,让你求出它的非递减序列有多少个. 设dp[i]表示以a[i]结尾的非递减子序列的个数,由题意我们可以写出状态转移方程: dp[i] = sum{dp[j] | 1&l ...

  6. CodeForces - 314C Sereja and Subsequences (树状数组+dp)

    Sereja has a sequence that consists of n positive integers, a1, a2, ..., an. First Sereja took a pie ...

  7. HDU 6348 序列计数 (树状数组 + DP)

    序列计数 Time Limit: 4500/4000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Subm ...

  8. [Codeforces261D]Maxim and Increasing Subsequence——树状数组+DP

    题目链接: Codeforces261D 题目大意:$k$次询问,每次给出一个长度为$n$的序列$b$及$b$中的最大值$maxb$,构造出序列$a$为$t$个序列$b$连接而成,求$a$的最长上升子 ...

  9. 【XSY2727】Remove Dilworth定理 堆 树状数组 DP

    题目描述 一个二维平面上有\(n\)个梯形,满足: 所有梯形的下底边在直线\(y=0\)上. 所有梯形的上底边在直线\(y=1\)上. 没有两个点的坐标相同. 你一次可以选择任意多个梯形,必须满足这些 ...

随机推荐

  1. Swift小记一

    1.输出地址 print(String(format: "%p", "temp")) 2.判断字符串是否为空串.是否为nil 为String添加一个分类 ext ...

  2. JS - 给数组的原型添加去掉重复元素的distinct方法

    /* 调用完该方法,原数组只留下非重复的数据 返回一个数组,里面是依次出现的重复元素 */Array.prototype.distinct = function () {    var removeA ...

  3. GNU 关闭 MMU 和 Icache 和 Dcache

    1. cp15 寄存器   disable Icache 和 Dcache . disable_MMU: MCR p15,0,r0,c7,c7,0 MRC p15,0,r0,c1,c0,0 bic r ...

  4. PHP siege 压测 QPS大小

    1.使用 PHP-FPM SOCKET的形式通讯 2.配置 PHP-FPM配置 [root@bogon php-fpm.d]# ls -al 总用量 drwxr-xr-x. root root 8月 ...

  5. 操作 Java 数组的 12 个最佳方法

    1.  声明一个数组 Java代码: String[] aArray = new String[5]; String[] bArray = {"a","b",& ...

  6. 笔记-unittest实战

    笔记-unittest实战 1.      框架图 2.      用例 编写自己的测试用例类,继承于基类 class ApiTestCase(unittest.TestCase): setUp方法会 ...

  7. Numpy数据存取与函数

    数据的CSV文件存取 多维数据的存取 NumPy的随机数函数 NumPy的统计函数 NumPy的梯度函数

  8. zeppelin的安装与使用

    想起马上就能回家了,心情是按捺不住的激动,唉,还是继续努力吧,其实不希望那么快就回家,感觉回去了就意味着马上就要回来了,人真的是神奇呀 今天我们来使用zeppelin,这个就是可以把我们查找的数据可以 ...

  9. Alter the structure of web pages with JavaScript

    Most of the DOM methods you've seen so far are useful for identifying elements. Both getElementById ...

  10. 1079: [SCOI2008]着色方案

    链接 思路 首先是dp,如果直接用每个种颜色的剩余个数做状态的话,复杂度为5^15. 由于c<=5,所以用剩余数量的颜色的种类数做状态:f[a][b][c][d][e][last]表示剩余数量为 ...