数列

题目描述

有一个长度为n 的排列,现在有一些位置的数已经模糊不清了,你只知道这个排列的逆序对个数是K,你能计算出总共有多少可能的排列吗?

折半

很经典的meet in the middle
不说了,记住有各种精妙实现压复杂度。

#include<cstdio>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=1000+10,mx=17297280+5;
struct dong{
    int x,y;
} g[mx];
int L[20000],R[20000],f[1000000+10];
int a[maxn],b[20],c[20][20],d[20],e[20],pl[6000][20],coun[20000];
bool bz[maxn];
int i,j,k,l,r,t,n,m,top,tot,cnt,wdc;
ll ans;
void pp(int x,int lim){
    int i;
    if (x==lim+1){
        ++cnt;
        fo(i,1,lim) pl[cnt][i]=d[i];
        return;
    }
    fo(i,1,lim) 
        if (!bz[i]){
            bz[i]=1;
            d[x]=i;
            pp(x+1,lim);
            bz[i]=0;
        }
}
void dfs(int x,int y,int lim){
    if (top-x+1<lim-y) return;
    int i,j,k,t,s;
    if (x==top+1){
        fo(i,1,cnt){
            fo(j,1,lim) e[pl[i][j]]=d[j];
            t=0;
            fo(j,1,lim) t+=c[l+j-1][e[j]];
            s=0;
            fo(j,1,lim){
                t+=coun[s>>e[j]];
                s+=1<<(e[j]-1);
            }
            /*zlt.x=s;zlt.y=t;
            g[zlt]++;*/
            g[++wdc].x=s;g[wdc].y=t;
        }
        return;
    }
    dfs(x+1,y,lim);
    if (y<lim){
        d[y+1]=x;
        dfs(x+1,y+1,lim);
    }
}
void dg(int x,int y,int lim){
    if (top-x+1<lim-y) return;
    int i,j,k,t,s;
    if (x==top+1){
        int S=0;
        fo(i,1,lim) S+=1<<(d[i]-1);
        S^=(1<<top)-1;
        if (!L[S]) return;
        fo(i,L[S],R[S]) f[g[i].y]++;
        fo(i,1,cnt){
            fo(j,1,lim) e[pl[i][j]]=d[j];
            t=0;
            fo(j,1,lim) t+=c[l+j-1][e[j]];
            s=0;
            fo(j,1,lim) s+=1<<(e[j]-1);
            s^=(1<<top)-1;
            fo(j,1,lim){
                t+=coun[s>>e[j]];
                s+=1<<(e[j]-1);
            }
            /*zlt.x=s;zlt.y=m-t;
            ans+=(ll)g[zlt];*/
            if (m-t<=1000000) ans+=(ll)f[m-t];
        }
        fo(i,L[S],R[S]) f[g[i].y]--;
        return;
    }
    dg(x+1,y,lim);
    if (y<lim){
        d[y+1]=x;
        dg(x+1,y+1,lim);
    }
}
bool cmp(dong a,dong b){
    return a.x<b.x;
}
int main(){
    freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n){
        scanf("%d",&a[i]);
        if (a[i]) bz[a[i]]=1;
    }
    fo(i,1,n)
        if (!bz[i]) b[++top]=i;
    fo(i,1,n){
        if (a[i]) continue;
        ++tot;
        fo(j,1,top)
            fo(k,1,n)
                if (a[k]){
                    if (k<i&&a[k]>b[j]) c[tot][j]++;
                    else if (k>i&&a[k]<b[j]) c[tot][j]++;
                }
    }
    fo(i,1,n-1)
        if (a[i])
            fo(j,i+1,n)
                if (a[j]&&a[i]>a[j]) m--;
    fo(i,1,top/2) bz[i]=0;
    pp(1,top/2);
    fo(i,0,16383){
        k=i;
        while (k){
            coun[i]++;
            k-=(k&-k);
        }
    }
    l=1;r=top/2;
    dfs(1,0,top/2);
    sort(g+1,g+wdc+1,cmp);
    fo(i,1,wdc){
        if (g[i].x!=g[i-1].x){
            R[g[i-1].x]=i-1;
            L[g[i].x]=i;
        }
    }
    R[g[wdc].x]=wdc;
    l=top/2+1;r=top;
    cnt=0;
    fo(i,1,top-top/2) bz[i]=0;
    pp(1,top-top/2);
    dg(1,0,top-top/2);
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值