[ZOJ3777] Problem Arrangement
题意
一个排列\(\{ a_i \}\)的权值为\(\sum_{i=1}^{n}p_{i,a_i}\)。
求期望随机重构排列多少次可以使这个排列的权值\(\ge m\)。
\(n\le12\)
sol
假设一共有\(s\)个排列满足权值\(\ge m\),那么一次随机后满足条件的概率就是\(\frac{s}{n!}\),所以答案就是\(\frac{n!}{s}\)。
考虑怎么求有多少个排列满足要求。
\(Meet\ in\ the\ middle\)。先处理前半段的所有填入方案,状态总数是\(A^{6}_{12}\)的。
然后再处理后半段的填入方案,取个补集之后就可以到前面去二分查了。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 4100;
int s[N][721],len[N],T,n,m,p[12][12],all,ans;
void dfs1(int u,int st,int sum)
{
if (u==n/2) {s[st][++len[st]]=sum;return;}
for (int i=0;i<n;++i)
if (~st&(1<<i)) dfs1(u+1,st|(1<<i),sum+p[u][i]);
}
void dfs2(int u,int st,int sum)
{
if (u==n/2-1)
{
st^=all;
ans+=len[st]+1-(lower_bound(s[st]+1,s[st]+len[st]+1,m-sum)-s[st]);
return;
}
for (int i=0;i<n;++i)
if (~st&(1<<i)) dfs2(u-1,st|(1<<i),sum+p[u][i]);
}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main()
{
T=gi();
while (T--)
{
n=gi();m=gi();all=(1<<n)-1;ans=0;
for (int i=0;i<n;++i)
for (int j=0;j<n;++j)
p[i][j]=gi();
memset(len,0,sizeof(len));
dfs1(0,0,0);
for (int i=0;i<=all;++i)
if (len[i]) sort(s[i]+1,s[i]+len[i]+1);
dfs2(n-1,0,0);
if (!ans) puts("No solution");
else {
int fz=1;for (int i=2;i<=n;++i) fz*=i;
int gg=gcd(fz,ans);printf("%d/%d\n",fz/gg,ans/gg);
}
}
return 0;
}