校内测试-NOIP模拟赛

Basketball1

数组劈成两半, 每一半中找出一些数, 求使左边一组异或和等于右边一组与和的方案数.

没看见劈成两半, 其实一开始想到正解, 但是如果交叉选人就没法通过, 所以 PASS 掉正解了.

fi,j,0 表示前 i 个数, 异或和为 j, 且 i 在集合中的方案数.

fi,j,1 表示 i 后面的数 (包括 i ) 与和等于 j, 且 i 在集合中的方案数.

辅助数组 gj 记录 (前/后) 缀和, 视情况改变.

左边正着算, 右边倒着算.

fi,jai,0+=gjfi,j&ai,1+=gj

统计答案时, 枚举断点, 左右异或/与和都等于同一个数的方案数相乘, 就是这个断点, 这个值的答案, 累加即可.

#include <algorithm>
#include <cstring>
#include <iostream>
#include <cstdio>
using namespace std;
const int MOD(1000000007);
inline unsigned int RD() {
	register unsigned int rdtp(0);
	register char rdch(getchar());
	while ((rdch > '9') || (rdch < '0')) {
		rdch = getchar();
	}
	while ((rdch <= '9') && (rdch >= '0')) {
		rdtp *= 10;
		rdtp += rdch - '0'; 
		rdch = getchar();
	}
	return rdtp;
}
unsigned int n, a[1005], g[1050]/*辅助数组*/, f[1005][1050][2];
unsigned long long Ans(0);
int main() {
	freopen("basketball1.in","r",stdin);
	freopen("basketball1.out","w",stdout);
	n = RD();
	if(!n) {
		printf("0\n");
		return 0;
	}
	memset(f, 0, sizeof(f));
	memset(g, 0, sizeof(g));
	for (register unsigned int i(1); i <= n; ++i) {
		a[i] = RD();
	}
	for (register unsigned int i(1); i <= n; ++i) {
		for(register unsigned int j(0); j < 1024; ++j) {
			f[i][j ^ a[i]][0] += g[j];
			f[i][j ^ a[i]][0] %= MOD;
		}
		++f[i][a[i]][0];
		for (register unsigned int j(0); j < 1024; ++j) {
			g[j] += f[i][j][0];
			g[j] %= MOD;
		}
	}
	memset(g, 0, sizeof(g));
	for (register unsigned int i(n); i >= 1; --i) {
		for(register unsigned int j(0); j < 1024; ++j) {
			f[i][j & a[i]][1] += g[j];
			f[i][j & a[i]][1] %= MOD;
		}
		++f[i][a[i]][1];
		for (register unsigned int j(0); j < 1024; ++j) {
			g[j] += f[i][j][1];
			g[j] %= MOD;
		}
	}
	memset(g, 0, sizeof(g));
	for(register unsigned int i(1); i < n; ++i) {
		for(register unsigned int j(0); j < 1024; ++j) {
			g[j] += f[i][j][0];
			g[j] %= MOD;
			Ans += (long long)g[j] * f[i + 1][j][1];
			Ans %= MOD;
		}
	}
	printf("%llu", Ans);
	return 0; 
}

Basketball2

给出人数 n, 权值上界 l, 需求权值和 k. 求可以取出权值和为 k 的权值方案数 (不是选人的方案数).

n,k20

排列组合以及其余的 DP 算法似乎都会涉及重复统计的问题.

dpi,state 表示前 i 位, State 所表示的权值和能取得的方案总数.

枚举第 i+1 个取的数 x

fi,(i|(i<<x)|(1<<(x1))) & ((1<<K)1)+=fi1,jfi+1,(|x|x)&()+=fi,j

如果取的数大于 k, 那么对于凑数来说, 都是等价的, 直接统计

fi,j+=fi1,j(lk)

O(n22n), 但是状压废状态比较多, 剪掉废状态即可 AC (否则 80)

#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int MOD(1000000007);
inline unsigned int RD() {
	register unsigned int rdtp(0);
	register char rdch(getchar());
	while ((rdch > '9') || (rdch < '0')) {
		rdch = getchar();
	}
	while ((rdch <= '9') && (rdch >= '0')) {
		rdtp *= 10;
		rdtp += rdch - '0'; 
		rdch = getchar();	
	}
	return rdtp;
}
unsigned int N, n, K, k, l, f[1050000][25], tmp;
unsigned long long Ans(0);
int main() {
	freopen("basketball2.in","r",stdin);
	freopen("basketball2.out","w",stdout);
	n = RD();
	k = RD();
	l = RD();
	K = min(k, l);
	N = (1 << k) - 1;
	memset(f, 0, sizeof(f));
	if(!l) {
			if(k){
				printf("0\n");
				return 0;
			}
		printf("1\n");
		return 0;
	}
	f[0][0] = 1;
	for (register unsigned int i(1); i <= n; ++i) {
		for (register unsigned int m(0); m <= N; ++m) {
			if(f[m][i - 1]) {
				for (register unsigned int j(0); j <= K; ++j) {
					tmp = (m/*原来能凑的*/ | (m << j/*加上j新凑出来的*/) | (1 << (j - 1))/*只选第i个人*/) & ((1 << k) - 1)/*防止溢出, 控制在所需范围以内*/;
					f[tmp][i] += f[m][i - 1];
					f[tmp][i] %= MOD;
				}
			}
		}
		if(l > k) {
			for (register unsigned int m(0); m <= N; ++m) {
				f[m][i] += ((long long)f[m][i - 1] * (l - k)) % MOD;//大于k的数对凑数无用 
				f[m][i] %= MOD;
			}
		}
	}
	for(register unsigned int i(1 << (k - 1)); i <= N; ++i) {
		Ans += f[i][n];
	}
	Ans %= MOD; 
	printf("%llu\n", Ans);
	return 0; 
}

Maze

一个 nn 的棋盘

  • # 是墙壁, 不能走
  • S 是问题, 多花 1 时间, 只需要解一次
  • K 是起点
  • T 是终点
  • 数字x, 是第 x 把钥匙, 要从第 1 把挨个拿.
  • . 是普通地格

地格之间四联通, 每次移动花 1 时间, 求从 K 出发拿到所有钥匙并到达 T 的最短时间.

0<n100,0m9,Snum5

这题由于 S 较少, 枚举每个 S 做还是不做 (不做就暂时设为 #), 然后直接 BFS 即可.

按顺序取钥匙, 增加一维表示已经拿到的钥匙, 将棋盘变成三维的, 有钥匙时有两个分支, 拿或是不拿, 不拿就在本层继续走, 拿就前往下一层.

fi,j,k 为第 (i,j) 点取到钥匙 k 的时间, 被更新就入队, 否则不入.

#include <algorithm>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int MOD(1000000007);
inline unsigned int RD() {
	register unsigned int rdtp(0);
	register char rdch(getchar());
	while ((rdch > '9') || (rdch < '0')) {
		rdch = getchar();
	}
	while ((rdch <= '9') && (rdch >= '0')) {
		rdtp *= 10;
		rdtp += rdch - '0'; 
		rdch = getchar();
	}
	return rdtp;
}
struct Pnt {
	unsigned int x, y, key;
	Pnt (unsigned int a = 0, unsigned int b = 0, unsigned int c = 0) {
		x = a;
		y = b;
		key = c; 
	}
};
queue<Pnt> Q;
//bool b[105][105];
unsigned int Cnt(0), sx, sy, tx, ty, S[7][2], m, n, f[105][105][10]/*f[i][j][k]:拿到第 k 把钥匙, 在i,j的最短时间*/, Ans(0x3f3f3f3f);
char s[105], a[105][105];
void Udt (const Pnt &x, const Pnt &y) {
	if (y.key + 1 == x.key) {
		if (f[x.x][x.y][x.key] > f[y.x][y.y][y.key] + 1) {//Pick
			f[x.x][x.y][x.key] = f[y.x][y.y][y.key] + 1;
			Q.push(x);
		}
	}
	if (f[x.x][x.y][y.key] > f[y.x][y.y][y.key] + 1) {//Not Pick
		f[x.x][x.y][y.key] = f[y.x][y.y][y.key] + 1;
		Q.push(Pnt(x.x, x.y, y.key));
	}
	return;
}
inline void Inst (Pnt x, const Pnt &y) {
	if (a[x.x][x.y] == 0x7f) {
		return;
	}
	x.key = (unsigned int)a[x.x][x.y];
	if(!x.key) {
		x.key = y.key;
	}
	Udt(x, y);
	return;
} 
void BFS() {
	Pnt x;
	Q.push(Pnt(sx, sy, 0));
	while (Q.size()) {
		x = Q.front();
		if (x.x - 1) {
			Inst(Pnt(x.x - 1, x.y, 0), x);
		}
		if (x.x + 1 <= n) {
			Inst(Pnt(x.x + 1, x.y, 0), x);
		}
		if (x.y - 1) {
			Inst(Pnt(x.x, x.y - 1, 0), x);
		}
		if (x.y + 1 <= n) {
			Inst(Pnt(x.x, x.y + 1, 0), x);
		}
		Q.pop();
	}
	return;
}
void DoS(unsigned int Dep, unsigned int Chost) {
	if(Dep > Cnt) {//枚举完了, 开始走 
		memset(f, 0x3f, sizeof(f));
		f[sx][sy][0] = Chost;
		BFS();
		Ans = min(f[tx][ty][m], Ans);
		return; 
	}
	a[S[Dep][0]][S[Dep][1]] = 0x7f; 
	DoS(Dep + 1, Chost);
	a[S[Dep][0]][S[Dep][1]] = 0;
	DoS(Dep + 1, Chost + 1);
	return;
} 
int main() {
	freopen("maze.in","r",stdin);
	freopen("maze.out","w",stdout);
	n = RD();
	m = RD();
	if(!n) {
		printf("0\n");
		return 0;
	}
	memset(a, 0, sizeof(a));
	for (register unsigned int i(1); i <= n; ++i) {
		gets(s);
		for (register unsigned int j(1); j <= n; ++j) {
			switch(s[j - 1]) {
				case '#':{
					a[i][j] = 0x7f;
					break;
				}
				case '.': {
					break;
				}
				case 'K': {
					sx = i;
					sy = j;
					break;
				}
				case 'S': {
					S[++Cnt][0] = i;
					S[Cnt][1] = j;
					break;
				}
				case 'T': {
					tx = i;
					ty = j;
					break;
				}
				default: {
					a[i][j] = s[j - 1] - '0';
					break;
				} 
			}
		}
	}
	memset(f, 0x3f, sizeof(f));
	DoS(1, 0);
	if(Ans >= 1000000000) {
		printf("impossible\n");
		return 0;
	}
	printf("%u\n", Ans);
	return 0; 
}
posted @   Wild_Donkey  阅读(77)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
历史上的今天:
2020-02-20 ybt1226 装箱问题
点击右上角即可分享
微信分享提示