题意:
给你一个n*n(n<=1000)的网格,初始第一行每一列有一只青蛙。
有n个膜,第个膜覆盖第l[i]列到第r[i]列。
每个膜只有一颗长生不老药。n个青蛙会一直往上跳,直到跳到第n行。如果一个膜覆盖所有列,则是好膜,否则是坏膜。
每个青蛙不能穿过超过10个坏膜,每个青蛙都要吃到长生不老药。求方法总数。
思路:状压dp。太难想了。。。
dp[i][j]表示第i列膜状态为j时的方法总数。
ne[i][j]=k表示第i行膜状态为j转移到第i+1行时膜状态为k。k=-1表示不合法。
初始:dp[1][1<<(第一列膜总数)-1]=1;
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2050;
const ll mo=105225319;
int n,m,k;
ll dp[maxn][maxn];
int ne[maxn][maxn];
vector<int>vc[maxn];
ll jc[maxn];
int l[maxn],r[maxn];
void init()
{
jc[0]=1;
for(ll i=1;i<maxn;i++)
jc[i]=(jc[i-1]*i)%mo;
}
int find(int x,int y)
{
for(int i=0;i<vc[x].size();i++)
if(vc[x][i]==y) return i;
return -1;
}
ll solve(int Q)
{
memset(ne,0,sizeof(ne));
for(int i=1;i<=n;i++)
if(vc[i].size()>10) return 0;
for(int i=1;i<n;i++)
{
int m1=vc[i].size(),m2=vc[i+1].size();
for(int k=0;k<m2;k++)
{
int f=find(i,vc[i+1][k]);
for(int s=0;s<(1<<m1);s++)
{
if(f==-1||(s&(1<<f)))
ne[i][s]|=(1<<k);
}
}
}
for(int i=1;i<=n;i++)
{
int m1=vc[i].size();
for(int s=0;s<(1<<m1);s++)
{
for(int j=0;j<m1;j++)
if(s&(1<<j)){
if(r[vc[i][j]]==i) ne[i][s]=-1;
}
}
}
memset(dp,0,sizeof(dp));
dp[1][(1<<vc[1].size())-1]=1;
for(int i=1;i<=n;i++)
{
int m1=vc[i].size();
for(int j=0;j<(1<<m1);j++)
{
for(int k=0;k<m1;k++)
if(j&(1<<k)){
int zt=j^(1<<k);
if(ne[i][zt]==-1) continue;
dp[i+1][ne[i][zt]]=(dp[i+1][ne[i][zt]]+dp[i][j])%mo;
}
int zt=ne[i][j];
if(zt!=-1) dp[i+1][zt]=(dp[i+1][zt]+dp[i][j])%mo;
}
}
//cout<<jc[Q]<<" "<<dp[n+1][0]<<endl;
ll ans=(jc[Q]*dp[n+1][0])%mo;
return ans;
}
int main()
{
init();
int T,cas=1;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&l[i]);
for(int i=1;i<=n;i++) scanf("%d",&r[i]);
for(int i=1;i<=n;i++) vc[i].clear();
int cnt=0;
for(int i=1;i<=n;i++)
{
if(l[i]==1&&r[i]==n) {cnt++;continue;}
for(int j=l[i];j<=r[i];j++)
{
vc[j].push_back(i);
}
}
printf("Case #%d: %lld\n",cas++,solve(cnt));
}
return 0;
}
/*
2
2
1 2
1 2
2
1 1
2 2
*/