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;
}
posted @ 2021-07-06 15:33  LPF'sBlog  阅读(74)  评论(0编辑  收藏  举报