1 条题解
-
0
题意已经很清楚了,就是问我们有多少种合法的握手方式,使得每个小朋友都能和两个人握手。
我们发现 ,所以我们考虑轮廓线 DP(不会的请翻到最后),每一位上 代表和右边的人握手,否则就不握。然后就可以做了。
我们发现 ,但是 ,所以我们可以猜测对于每两个有老师的列的中间那些没有老师的列,我们可以快速解决,考虑矩阵优化 DP。我们发现对于没有老师的列之间转移是平凡的,所以我们可以预处理出从 转移到 的矩阵,然后就可以做了。
所以整道题的思路就是先预处理出每一个状态 转移到另一个状态 的矩阵。然后对于有老师的一列做轮廓线 DP,对于两个老师之间的空列就直接矩阵快速幂就可以了。
时间复杂度 ,其中 为所有连续空列的长度之积。因为 。又因为常数比较大,所以可能超时,考虑因为每次做矩阵快速幂的时候会重复算多次转移矩阵的 次方,故我们可以通过预处理将其存下来,时间复杂度就优化成 。可以通过此题。
#include<bits/stdc++.h> using namespace std; #define N 100005 #define intl long long #define mod (1000000007) #define For(i,a,b) for(intl i=a;i<=b;i++) #define deo(i,a,b) for(intl i=a;i>=b;i--) intl read() { intl x=0,k=1;char ch=getchar(); while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return x*k; } intl n, m, k; map<intl,vector<intl>> pos; void get(auto &res, intl s) { vector<vector<intl>> dp(1ll<<n, vector<intl>(2,0)); For(i,0,(1ll<<n)-1) dp[i][0] = res[i]; For(i,0,n-1) { intl flg = (s>>i)&1; vector<vector<intl>> tmp(1ll<<n, vector<intl>(2,0)); For(j,0,(1ll<<n)-1) For(v,0,1) if(dp[j][v]) { intl l = (j>>i)&1; For(r,0,1) For(vt,0,1) if(!(i == n-1 && vt)){ if(flg) { if(l + r + v + vt == 0) (tmp[j][vt] += dp[j][v]) %= mod; } else { if(l + r + v + vt == 2) (tmp[(j&(~(l<<i))|(r<<i))][vt] += dp[j][v]) %= mod; } } } dp = tmp; } For(i,0,(1ll<<n)-1) res[i] = dp[i][0]; } struct Mat{ intl a[256][256]; Mat operator * (const Mat&b) const { Mat res;memset(res.a,0,sizeof res.a); For(k,0,(1ll<<n)-1) For(i,0,(1ll<<n)-1) if(a[i][k]) For(j,0,(1ll<<n)-1) if(b.a[k][j]) (res.a[i][j] += a[i][k] * b.a[k][j]) %= mod; return res; } vector<intl> operator + (const vector<intl>&b) const { vector<intl> res((1ll<<n),0); For(i,0,(1ll<<n)-1) For(j,0,(1ll<<n)-1) (res[i] += a[i][j]*b[j]) %= mod; return res; } }mat[32]; void Fpowv(intl b,auto &res) { intl cnt = 0; for(;b;b>>=1,cnt ++) if(b&1) res = mat[cnt] + res; } int main() { n = read(), m = read(), k = read(); vector<intl> dp(1ll<<n); For(i,1,k) { intl x = read()-1, y = read(); pos[y].push_back(x); } For(i,0,(1ll<<n)-1) { vector<intl> init(1ll<<n,0); init[i] = 1; get(init,0); For(j,0,(1ll<<n)-1) mat[0].a[j][i] = init[j]; } For(i,1,31) mat[i] = mat[i-1]*mat[i-1]; intl las = 1;dp[0] = 1; for(auto [c,vec]:pos) { if(c > las) Fpowv(c-las,dp); intl mask = 0; for(auto x:vec) mask |= (1ll<<x); get(dp, mask); las = c + 1; } if(las <= m) Fpowv(m-las+1, dp); printf("%lld\n", dp[0]); return 0; }
- 1
信息
- ID
- 5994
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者