P5392 [Cnoi2019]雪松树之约
首先分析一下给出的图的形态,限制
观察数据范围,
先预处理出每层满足独立集的状态,发现当
手动模拟发现有些情况其实是本质相同的。
例如上面两个环,忽略编号时,右图中的环可由左图中的环逆时针旋转
考虑合并这种旋转以后能重合的本质相同的环,再跑一遍发现可用状态只剩下 只要常数小刚好能过?
然后想压缩掉这些本质相同的状态后如何转移。对于一些本质相同的状态,我们从中选出其中一个作为这些状态的代表环,而这个代表环是有编号对应的。我们设
转化为矩阵,容易发现
最后统计答案时,对于第
注意常数,尽量减少取模,时间复杂度
点击查看代码
#include <bits/stdc++.h> using namespace std; #define ll long long const ll SIZE = 200005; const ll mod = 998244353; ll L, n; bool mp[150005]; bool mp1[150005]; ll cntm[150005]; ll a[SIZE], id[150005], tot; inline ll rd(){ ll f = 1, x = 0; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return f*x; } struct node{ ll o[212][212]; }zy; node jl; node mul(node A, node B){ for(int i = 0; i <= tot; i++) for(int j = 0; j <= tot; j++){ jl.o[i][j] = 0; for(ll k = 0; k <= tot; k++){ jl.o[i][j] += A.o[i][k] * B.o[k][j] % mod; } jl.o[i][j] %= mod; } return jl; } node power(node x, ll y){ node j2; for(int i = 0; i <= tot; i++) for(int j = 0; j <= tot; j++) j2.o[i][j] = (i==j); while(y){ if(y & 1) j2 = mul(j2, x); x = mul(x, x); y >>= 1; } return j2; } int main(){ L = rd(), n = rd(); for(int i = 0; i < (1<<n); i++){ ll now = i; bool ff = 0; for(ll j = 2; j <= n; j++){ if((i&(1<<(j-1))) && (i&(1<<(j-2)))){ ff = 1; break; } } if((i&1) && (i&(1<<(n-1)))) ff = 1; if(!ff){ //同一个环内满足两两不相邻 for(ll j = 1; j < n; j++){ now = ((now&1)<<(n-1))|(now>>1); if(mp[now]){ cntm[id[now]]++;//更新循环意义下相同方案的个数 ff = 1; break; } } } if(!ff){//出现一种新的本质不同的环 cntm[tot+1] = 1; mp[i] = 1; a[++tot] = i; id[a[tot]] = tot; } } for(int i = 1; i <= tot; i++){//枚举可能的转移,构造转移矩阵 for(int j = 1; j <= tot; j++){ if((a[i]&a[j]) == 0) zy.o[i][j]++; if(a[i] == 0) continue; ll now = a[i], st = a[i]; bool ff = 0; for(int h = 1; h < n; h++){ now = ((now&1)<<(n-1))|(now>>1); if(ff && now == st) break; if((now&a[j]) == 0) zy.o[i][j]++; ff = 1; } } } node ans = power(zy, L-1); ll aans = 0; for(int i = 1; i <= tot; i++){ ll jl = 0; for(int j = 1; j <= tot; j++){ jl = jl + ans.o[j][i]; if(jl >= mod) jl -= mod; } jl = jl * cntm[i] % mod; //统计最顶层为状态i的方案数 aans = aans + jl; if(aans >= mod) aans -= mod; } printf("%lld", aans); return 0; }
本文作者:Semorius
本文链接:https://www.cnblogs.com/Semorius/p/17538285.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步