1 条题解
-
0
自动搬运
来自洛谷,原作者为

hdxrie
**搬运于
2025-08-24 22:05:53,当前版本为作者最后更新于2018-10-12 14:29:56,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
注意:
题解中算复杂度所提到的均为如下公式:
$$q=\sum_{i=1}^{k-1}C_k^i\times \sum_{j=1}^{k-i}C_{k-i}^j $$算法1:
暴力搜索所有情况,虽然看起来复杂度为,但是很多状态是不合法的,实际上合法方案数不会超过,那么暴力搜索的复杂度也不会太多。
时间复杂度,期望得分:分。
算法2:
读题发现很小,可以想到状压,考虑定义转移,令表示考虑到第天,当天吃的菜品集合为时的合法方案数,令为前一天吃的菜品集合,那么转移可以如下表示:
采用子集枚举,则转移的时间复杂度为,当时取到上界为。
时间复杂度为,期望得分:分。
算法3:
观察到很特殊,如果令为那一天提供的菜品数量,规律可以总结为以下种情况:
$$ans=\begin{cases}2\ (n\geq1,p=2)\\0\ (n>1,p<2)\\1\ (n=1,p=1)\end{cases} $$特判回答即可。
时间复杂度,加上算法,期望得分:分。
算法4:
观察后面的测试点,看起来需要一个时间复杂度为的算法,但是这只是出题人的陷阱,因为转移的复杂度已经不能再优化了
只是出题人不知道怎么优化了,我们需要想别的办法。观察所有的状态转移,如果以天为单位,可以发现转移是一样的,那么可以想到构造矩阵进行优化。对于制定了天的菜谱,每一天都花的时间复杂度构造一个转移矩阵,先将这天的矩阵乘起来,然后求乘完后矩阵的次方,对于剩余的天,因为不是一个完整的天,所以在之前合并矩阵的时候记录一下前天的矩阵合并后的样子,最后再将记录的矩阵乘上去,将所有方案累加就是答案。预处理时间复杂度,转移时间复杂度$O(2^{3m}log\left\lfloor\frac{n}{k}\right\rfloor+2^{2m})$,期望得分:分。
实际上加个循环展开就可以过了。算法5:
由于合并矩阵的复杂度太高,我们得换一种方式构造一个周期的转移矩阵,实际上我们可以尝试用构造,我们枚举开头那一天的所有情况,然后对于每一个情况单独进行一次天的算法的,这样就能求出当状态为的时候,经过天后能对哪些状态有贡献且贡献为多少,这样能在的时间复杂度内构造出转移矩阵,对于剩下的天,在中途记录一下只转移天的矩阵,那么只需要在最后再乘上它,我们就得到了答案。
预处理时间复杂度,转移时间复杂度$O(2^{3m}log\left\lfloor\frac{n}{k}\right\rfloor+2^{2m})$,期望得分:分。
#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; }顺便吐槽洛谷的好猛...
- 1
信息
- ID
- 3970
- 时间
- 1000ms
- 内存
- 8MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者