题解 P4982 【规划】
注意:
题解中算复杂度所提到的
算法1:
暴力搜索所有情况,虽然看起来复杂度为
时间复杂度
算法2:
读题发现
采用子集枚举,则转移的时间复杂度为
时间复杂度为
算法3:
观察到
特判回答即可。
时间复杂度
算法4:
观察后面的测试点,看起来需要一个时间复杂度为只是出题人不知道怎么优化了,我们需要想别的办法。观察所有的状态转移,如果以
预处理时间复杂度
实际上加个循环展开就可以过了。
算法5:
由于合并矩阵的复杂度太高,我们得换一种方式构造一个周期的转移矩阵,实际上我们可以尝试用
预处理时间复杂度
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=130,M=310,mod=1e9+7;
int n,m,k,S,l1,l2,out,have[M],ans[N];
int tran[N][N],tem[N][N],day[N][N],dp[2][N];
void merge1()
{
memset(tem,0,sizeof(tem));
for(int i=1;i<=S;i++)
for(int j=1;j<=S;j++)
for(int p=1;p<=S;p++)
(tem[i][j]+=1ll*tran[i][p]*tran[p][j]%mod)%=mod;
for(int i=1;i<=S;i++)
for(int j=1;j<=S;j++)
tran[i][j]=tem[i][j];
}
void merge2()
{
memset(tem[0],0,sizeof(tem[0]));
for(int i=1;i<=S;i++)
for(int j=1;j<=S;j++)
(tem[0][i]+=1ll*ans[j]*tran[j][i]%mod)%=mod;
for(int i=1;i<=S;i++)
ans[i]=tem[0][i];
}
void merge3()
{
memset(tem[0],0,sizeof(tem[0]));
for(int i=1;i<=S;i++)
for(int j=1;j<=S;j++)
(tem[0][i]+=1ll*ans[j]*day[j][i]%mod)%=mod;
for(int i=1;i<=S;i++)
ans[i]=tem[0][i];
}
void power(int p)
{
for(;p;p>>=1,merge1())
if(p&1)merge2();
}
int main()
{
scanf("%d%d%d",&n,&m,&k);S=(1<<m)-1;
for(int i=1;i<=k;i++)
{
scanf("%d",&l1);
for(int j=1;j<=l1;j++)
{
scanf("%d",&l2);
have[i]|=1<<(l2-1);
}
}
for(int i=have[1];i;i=(i-1)&have[1])
{
int now=0;
memset(dp,0,sizeof(dp));dp[now][i]=1;
for(int j=2;j<=k;j++)
{
memset(dp[now^1],0,sizeof(dp[now^1]));
for(int p=have[j];p;p=(p-1)&have[j])
for(int q=(have[j-1]|p)^p;q;q=(q-1)&((have[j-1]|p)^p))
(dp[now^1][p]+=dp[now][q])%=mod;
now^=1;if((n-1)%k==j-1)
for(int p=1;p<=S;p++)
day[i][p]=dp[now][p];
}
memset(dp[now^1],0,sizeof(dp[now^1]));
for(int p=have[1];p;p=(p-1)&have[1])
for(int q=(have[k]|p)^p;q;q=(q-1)&((have[k]|p)^p))
(dp[now^1][p]+=dp[now][q])%=mod;
now^=1;for(int j=1;j<=S;j++)
tran[i][j]=dp[now][j];
}
for(int i=have[1];i;i=(i-1)&(have[1]))ans[i]=1;
power((n-1)/k);if((n-1)%k!=0)merge3();
for(int i=1;i<=S;i++)(out+=ans[i])%=mod;
printf("%d\n",out);
return 0;
}
顺便吐槽洛谷的