。。这题有毒。
既然不超过十四个位置,我们折半搜索一下,先预处理出所有的可以用的数字和位置,注意这两个的下标是相同的!!!
然后再预处理出c[i][j]表示把remain[i]放在b[j]位置的逆序对数量,当然,我们要先把已有的逆序对数量减去。
然后分成两半,左右各跑全排列,算方案数。
具体的话,就是分成两半(注意组合算,否则会算重),然后先算左边,然后右边就用左边没有用过的数字,至于逆序对数,可以用预处理的c快速算出。
折半搜索就是meet in the middle。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n,m;
const int N=1e5+5;
typedef long long ll;
ll ans;
bool bz[N],vis[N],flag[N];
int g[N],q[N];
int b[N],a[N],c[30][30],remain[N],sum[N],d[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9')
{
if(ch=='-')f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline void dfs2(int x,int y)
{
if (x>y)
{
int tmp=0;
fo(i,1,y)
{
tmp+=c[g[i]][i]+g[i]-1;
fo(j,1,i-1)if (g[j]>g[i])tmp++;
fo(j,1,y)if (g[j]<g[i])tmp--;
}
sum[tmp]++;
return;
}
fo(i,1,y)
if (!vis[i])
{
g[x]=d[i];
vis[i]=1;
dfs2(x+1,y);
vis[i]=0;
}
}
inline void dfs3(int x,int y)
{
if (x>y)
{
int tmp=0;
fo(i,1,y)
{
tmp+=c[g[i]][i+n/2];
fo(j,1,i-1)
if (g[j]>g[i])tmp++;
}
if (tmp<=m)ans+=sum[m-tmp];
return;
}
fo(i,1,y)
if (!vis[i])
{
g[x]=q[i];
vis[i]=1;
dfs3(x+1,y);
vis[i]=0;
}
}
inline void solve()
{
memset(sum,0,sizeof(sum));
dfs2(1,n/2);
int cnt=0;
fo(i,1,n/2)vis[d[i]]=1;
fo(i,1,n)if (!vis[i])q[++cnt]=i;
fo(i,1,n/2)vis[d[i]]=0;
dfs3(1,cnt);
}
inline void dfs1(int x,int last)
{
if (x==n/2)
{
solve();
return;
}
fo(i,last+1,n)
if (!flag[i])
{
flag[i]=1;
d[x+1]=i;
dfs1(x+1,i);
flag[i]=0;
}
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
scanf("%d%d",&n,&m);
int tot=0;
fo(i,1,n)
{
scanf("%d",&a[i]);
if (!a[i])b[++tot]=i;
else bz[a[i]]=1;
}
int cnt=0;
fo(i,1,n)
if (!bz[i])
remain[++cnt]=i;
fo(i,1,n-1)
if (a[i])
fo(j,i+1,n)
if (a[j]&&a[j]<a[i])m--;
fo(i,1,tot)
fo(j,1,tot)//put remain[i] into b[j]
{
fo(k,1,b[j]-1)
if (a[k]>remain[i])c[i][j]++;
fo(k,b[j]+1,n)
if (a[k]&&a[k]<remain[i])c[i][j]++;
}
n=tot;
dfs1(0,0);
printf("%lld\n",ans);
return 0;
}