zoukankan      html  css  js  c++  java
  • BZOJ4361 isn

    题目链接:戳我

    我们先处理出来长度为i的子序列分别都有多少个,记(g[i])表示长度为i的不降子序列个数。

    (a[i])表示第i个数的数值。设(f[i][j])表示以a[i]这个数结尾,且序列长度为j的子序列个数。

    那么就有DP式为(f[i][j]=sum f[k][j-1](a[k]>=a[i]))

    对于一个确定的不降子序列,如果它的长度为i的话,那么不考虑排除不合法的,贡献数为(g[i]*(n-i)!)。现在考虑什么情况不合法呢?显然是有可能在序列长度为i+1的时候就已经不降了qwq,我们没有办法再删,但还是删掉了。所以要减去(g[i+1]*(i+1)*(n-i-1)!)

    为什么减去不合法情况的时候只需要考虑(i+1)长度,却不考虑那些(i+2,i+3)的呢?因为如果那些更长的删掉一个数之后肯定还是合法的,而它们(比如说长度为i+2)删掉之后的情况个数都已经在删掉这个数之后的长度的情况中(i+1长度)计算到了。如果减去(i+1长度的),再减去(i+2长度的),就相当于多减去了qwqwq$$

    代码如下:

    (O(n^3))做法:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define MAXN 2010
    #define mod 1000000007
    using namespace std;
    int n,ans;
    int f[MAXN][MAXN],g[MAXN],dp[MAXN],a[MAXN],c[MAXN];
    inline void init()
    {
    	c[0]=1;
    	for(int i=1;i<=n;i++) c[i]=1ll*c[i-1]*i%mod;
    }
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("ce.in","r",stdin);
    	freopen("ce.out","w",stdout);
    	#endif
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for(int i=1;i<=n;i++) f[i][1]=1;
    	for(int i=1;i<=n;i++)
    		for(int j=2;j<=i;j++)
    			for(int k=1;k<i;k++)
    			{
    				if(a[k]>a[i]) continue;
    				f[i][j]=(f[i][j]+f[k][j-1])%mod;
    			}
    	for(int j=1;j<=n;j++)
    		for(int i=1;i<=n;i++)
    			g[j]=(g[j]+f[i][j])%mod;
    	init();
    	for(int i=1;i<=n;i++)
    		ans=(ans+1ll*g[i]*c[n-i]%mod-1ll*g[i+1]*(i+1)%mod*c[n-i-1]%mod+mod)%mod;
    	printf("%d
    ",ans);
    	return 0;
    }
    

    树状数组优化后(O(n^2logn))做法:
    记得要先对数值离散化qwqwq

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define MAXN 2010
    #define mod 1000000007
    using namespace std;
    int n,ans;
    int f[MAXN][MAXN],g[MAXN],dp[MAXN],a[MAXN],c[MAXN],tree[MAXN],pre[MAXN];
    inline void init()
    {
        c[0]=1;
        for(int i=1;i<=n;i++) c[i]=1ll*c[i-1]*i%mod;
    }
    inline void add(int x,int k)
    {
        for(int i=x;i<=n;i+=i&(-i))
            tree[i]=(tree[i]+k)%mod;
    }
    inline int query(int x)
    {
        int cur_ans=0;
        for(int i=x;i;i-=i&(-i))
            cur_ans=(cur_ans+tree[i])%mod;
        return cur_ans%mod;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        freopen("ce.out","w",stdout);
        #endif
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        memcpy(pre,a,sizeof(a));
        pre[n+1]=0;
        sort(&pre[1],&pre[2+n]);
        int tot=unique(&pre[1],&pre[2+n])-pre-1;
        for(int i=0;i<=n;i++) a[i]=lower_bound(&pre[1],&pre[1+tot],a[i])-pre;
        for(int i=1;i<=n;i++) f[i][1]=1;
        for(int j=2;j<=n;j++)
        {
            memset(tree,0,sizeof(tree));
            for(int i=1;i<=n;i++)
            {
                add(a[i-1],f[i-1][j-1]);
                f[i][j]=query(a[i])%mod;
            }
        }
        for(int j=1;j<=n;j++)
            for(int i=1;i<=n;i++)
                g[j]=(g[j]+f[i][j])%mod;
        init();
        for(int i=1;i<=n;i++)
            ans=(ans+1ll*g[i]*c[n-i]%mod-1ll*g[i+1]*(i+1)%mod*c[n-i-1]%mod+mod)%mod;
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    event.preventDefault()
    laravel 授权使用gate门类
    Redis在Laravel项目中的应用实例详解
    Laravel实现找回密码及密码重置的例子
    2016-2017中国房地产走势大数据报告亮相
    特许金融分析师 (CFA) 持证人现在一般在做什么工作?职业分布是怎样的?
    工作中最常用的Excel函数公式大全
    151项国家职业资格目录清单公示
    中国 世界十大投资风云人物,谁是自己的指路明灯!
    世界十大投资风云人物,你知道几个?
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10428977.html
Copyright © 2011-2022 走看看