[JZOJ5086]数列

来源:互联网 发布:虎扑认证淘宝店铺 编辑:程序博客网 时间:2024/05/21 17:06

题目大意

有一个长度为n的排列,但是有一些位置的数字还没有确定。你需要统计逆序对为K的可能的排列个数。

1n103,1K109
保证没有确定的位数m不超过14


题目分析

既然没有确定的位数最多只有14,那么考虑meet in middle。
搜出后面位置填了什么,然后将其用随便一种方式存下来。
然后搜出前面的位置填的数,然后在前面存下来的那里找到对应于没有出现过的数的状态,瞎合并一下。
如果你各种细节实现得好一点,你是可以做到O(m!(m/2)!×m2)的。


代码实现

#include <algorithm>#include <iostream>#include <cstdio>using namespace std;typedef long long LL;const int N=1005;const int L=14;const int C=N*L/2;const int S=3500;const int P=67;const int A=1<<L;int pre[N][N],suf[N][N],tot[N];int per[N],rem[N],pos[N],p[N];bool exist[N],used[N];int f[S][C];int id[A];int n,K,cnt,l,cur;LL ans;int lowbit(int x){return x&-x;}int bitcount(int s){    int ret=0;    for (;s;s-=lowbit(s)) ++ret;    return ret;}void prepare(){    for (int i=1;i<=n;++i)    {        for (int j=1;j<=n;++j) pre[i][j]=pre[i-1][j];        if (per[i]) ++pre[i][per[i]];    }    for (int i=1;i<=n;++i)        for (int j=n;j>=1;--j)            pre[i][j]+=pre[i][j+1];    for (int i=n;i>=1;--i)    {        for (int j=1;j<=n;++j) suf[i][j]=suf[i+1][j];        if (per[i]) ++suf[i][per[i]];    }    for (int i=1;i<=n;++i)        for (int j=1;j<=n;++j)            suf[i][j]+=suf[i][j-1];    for (int i=1;i<=n;++i)        if (per[i]) cur+=pre[i][per[i]+1];    l=pos[0]/2;    for (int s=0;s<1<<pos[0];++s)        if (bitcount(s)==pos[0]-l)            id[s]=++cnt;}int tmp[20],rec[20];void load(int curr){    int s=0;    for (int i=l+1;i<=pos[0];++i) s|=1<<p[i]-1;    int sid=id[s],ret=curr;    for (int i=l+1;i<=pos[0];++i) ret+=pre[pos[i]][rem[p[i]]+1]+suf[pos[i]][rem[p[i]]-1];    tmp[0]=0,rec[0]=0;    for (int i=1;i<=pos[0];++i)        if (!((s>>i-1)&1)) tmp[++tmp[0]]=rem[i];        else rec[++rec[0]]=rem[i];    sort(tmp+1,tmp+1+tmp[0]),sort(rec+1,rec+1+rec[0]);    for (int i=1,ptr=1;i<=rec[0];++i)    {        for (;ptr<=tmp[0]&&tmp[ptr]<rec[i];++ptr);        ret+=tmp[0]+1-ptr;    }    ++f[sid][ret];}void dfs(int x,int curr){    if (x==pos[0]+1)    {        load(curr);        return;    }    int total=0;    for (int i=1;i<=rem[0];++i)        if (!used[i]) used[i]=1,p[x]=i,dfs(x+1,curr+x-l-1-total),used[i]=0;        else ++total;}void solve(int curr){    int s=0;    for (int i=1;i<=l;++i) s|=1<<p[i]-1;    int sid=id[((1<<pos[0])-1)^s],ret=curr;    for (int i=1;i<=l;++i) ret+=pre[pos[i]][rem[p[i]]+1]+suf[pos[i]][rem[p[i]]-1];    if (K-cur-ret>=0&&K-cur-ret<=n*(pos[0]-l)) ans+=f[sid][K-cur-ret];}void calc(int x,int curr){    if (x==l+1)    {        solve(curr);        return;    }    int total=0;    for (int i=1;i<=rem[0];++i)        if (!used[i]) used[i]=1,p[x]=i,calc(x+1,curr+x-1-total),used[i]=0;        else ++total;}int main(){    freopen("sequence.in","r",stdin),freopen("sequence.out","w",stdout);    scanf("%d%d",&n,&K);    for (int i=1;i<=n;++i)    {        scanf("%d",&per[i]);        if (!per[i]) pos[++pos[0]]=i;        else exist[per[i]]=1;    }    for (int i=1;i<=n;++i) if (!exist[i]) rem[++rem[0]]=i;    prepare(),dfs(l+1,0),calc(1,0);    printf("%lld\n",ans);    fclose(stdin),fclose(stdout);    return 0;}
0 0
原创粉丝点击