2021.7.6 杂题选讲
horse
数据范围 \(n\leq 50,m\leq 10^9\)。
看上去就很矩阵乘法,但是状态实在难设计。
设 \(f(i,j)\) 表示跳到前 \(i\) 列中与 \(i\) 同奇偶第 \(j\) 行的方案数的前缀和。
那么 \(f(i,j)=f(i-1,j)+f(i-1,j+1)+f(i-1,j-1)+f(i-2,j)\)。
解释一下,该列可以从前面任意一个不同奇偶的列跳过来,所以有前三项,因为是前缀和,所以有第四项。
发现 \(i\) 仅仅和前两列有关,所以可以两列一起推,构造一个 \(1\times 2n\) 的答案矩阵(前 \(n\) 个是第 \(i\) 列,后 \(n\) 个是第 \(i+1\) 列)。
然后推一下转移矩阵,发现它由四个小矩阵构成,左下和右上是单位矩阵,右下是零矩阵,左上根据方程推推就出来了。
由于是前缀和,所以我们只推到第 \(m-1\) 列,然后 \(ans=f(n)+f(n-1)\),最后一行只能从第 \(n/n-1\) 行推过来。
记得特判 \(m=2\) 的情况。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 110;
const int MOD = 30011;
int n, m, f[N];
int read(){
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
struct Matrix{
int a[N][N];
Matrix(){memset(a, 0, sizeof(a));}
Matrix operator * (const Matrix &b) const{
Matrix c;
for(int i = 1; i <= (n << 1); i ++)
for(int k = 1; k <= (n << 1); k ++) if(a[i][k])
for(int j = 1; j <= (n << 1); j ++) if(b.a[k][j])
c.a[i][j] = (c.a[i][j] + a[i][k] * b.a[k][j] % MOD) % MOD;
return c;
}
};
Matrix Pow(Matrix A, int b){
Matrix c;
for(int i = 1; i <= (n << 1); i ++) c.a[i][i] = 1;
for(; b; b >>= 1){
if(b & 1) c = c * A;
A = A * A;
}
return c;
}
void Mul(int f[N], Matrix A){
int c[N];
memset(c, 0, sizeof(c));
for(int j = 1; j <= (n << 1); j ++)
for(int k = 1; k <= (n << 1); k ++)
c[j] = (c[j] + f[k] * A.a[k][j] % MOD) % MOD;
memcpy(f, c, sizeof(c));
}
int main(){
n = read(), m = read();
if(m == 2) {
if(n <= 2) puts("1");
else puts("0");
return 0;
}
f[1] = 1, f[2] = 1, f[n + 1] = 1;
Matrix A;
for(int i = 1; i <= n; i ++){
A.a[i][i] = 1;
if(i != 1) A.a[i - 1][i] = 1;
if(i != n) A.a[i + 1][i] = 1;
A.a[i][i + n] = 1;
A.a[i + n][i] = 1;
}
Mul(f, Pow(A, m - 3));
printf("%d\n", (f[n] + f[n - 1]) % MOD);
return 0;
}
spring
判断 \(n\) 个有 \(6\) 个数的集合中,恰好有 \(k\) 个数相等的集合对数。
恰好 \(k\) 个不太好掌握,不妨直接枚举哪些位相等(Hash 判断),然后容斥一下。
于是没了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N = 100010;
const ULL P = 1333331;
int n, m, a[N][10];
LL ans = 0, C[10][10];
int read(){
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
struct Hash{
int MOD = 99991;
int head[N], cnt;
struct Edge{int nxt, val; ULL to;} ed[N];
void clear(){
cnt = 0;
memset(head, 0, sizeof(head));
}
void insert(ULL x){
int u = x % MOD;
for(int i = head[u]; i; i = ed[i].nxt)
if(ed[i].to == x){
ed[i].val ++;
return;
}
ed[++ cnt].nxt = head[u];
ed[cnt].to = x, ed[cnt].val = 1;
head[u] = cnt;
}
} H;
void Work(int s){
int cnt = 0;
for(int i = 0; i < 6; i ++)
if(s >> i & 1) cnt ++;
if(cnt < m) return;
H.clear();
for(int i = 1; i <= n; i ++){
ULL sum = 0;
for(int j = 0; j < 6; j ++)
if(s >> j & 1) sum = sum * P + a[i][j];
H.insert(sum);
}
LL num = 0;
for(int i = 1; i <= H.cnt; i ++)
num += 1LL * H.ed[i].val * (H.ed[i].val - 1) / 2;
num *= C[cnt][m];
if((cnt & 1) == (m & 1)) ans += num;
else ans -= num;
}
int main(){
C[0][0] = 1;
for(int i = 1; i <= 6; i ++){
C[i][0] = C[i][i] = 1;
for(int j = 1; j < i; j ++)
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
n = read(), m = read();
for(int i = 1; i <= n; i ++)
for(int j = 0; j < 6; j ++) a[i][j] = read();
for(int i = 0; i < (1 << 6); i ++)
Work(i);
printf("%lld\n", ans);
return 0;
}