[JZOJ5600] 【NOI2018模拟3.26】Arg

Description

给出一个长度为 m 的序列 A, 请你求出有多少种 1…n 的排列, 满足 A 是它的一个 最长不降子序列.
对于 100% 的数据, 1 ≤ m ≤ n ≤ 15.

Solution

看到数据范围,这很状压

最长不降子序列的一种求法是:使用附加数组D[i],表示当前长度为i的最长不降子序列最小的结尾是多少,显然它是单调递增的。每插入一个数就二分找到第一个大于它的位置替换。

我们可以设状态S1,第i位为1表示它在D中出现了,出现的位置就是1~i位1的数量

现在就可以设递推状态 f(S,S1) 表示当前用了S中的数,D的状态为S1的方案数

转移很容易转移,枚举这一位选什么直接更新
注意到S1一定是S的子集,于是可以用三进制表示
复杂度 O(3nn) 实际上远远不到

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 16
#define M 14348910
#define LL long long
using namespace std;
int n,m,cf[N],a[N],wz[N],cf2[N];
LL f[M],ans;
void dfs(int k,int lim,int v)
{
    if(k>n)
    {
        if(lim==0) ans+=f[v];
        return;
    }
    v+=cf[k-1];
    dfs(k+1,lim,v);
    v+=cf[k-1];
    dfs(k+1,lim-1,v);
}
int main()
{
    cin>>n>>m;
    fo(i,1,m) scanf("%d",&a[i]),wz[a[i]]=i;
    cf[0]=cf2[0]=1;
    fo(i,1,n) cf[i]=cf[i-1]*3,cf2[i]=cf2[i-1]*2;
    f[0]=1;
    fo(i,0,cf2[n]-2)
    {
        bool pd=1,c1=1;
        fo(j,1,m) 
        {
            if((i&cf2[a[j]-1])==0) pd=0;
            else if(!pd)
            {
                c1=0;
                break;
            }
        }
        if(c1)
        {
            for(int i1=i;1;i1=(i1-1)&i)
            {
                int vi=0;
                fo(j,1,n) 
                {
                    if(i1&cf2[j-1]) vi+=cf[j-1];
                    if(i&cf2[j-1]) vi+=cf[j-1];
                } 
                if(f[vi])
                fo(j,1,n)
                {
                    if(vi/cf[j-1]%3==0)
                    {
                        int v=vi+2*cf[j-1];
                        fo(p,j+1,n) 
                            if(v/cf[p-1]%3==2) 
                            {
                                v-=cf[p-1];
                                break;
                            }
                        f[v]+=f[vi];
                    }
                }
                if(i1==0) break;
            }
        }
    }
    dfs(1,m,0);
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值