ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

校内测试-NOIP模拟赛

2021-02-20 09:04:47  阅读:193  来源: 互联网

标签:校内 NOIP int register unsigned rdch key return 模拟


Basketball1

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

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

设 \(f_{i, j, 0}\) 表示前 \(i\) 个数, 异或和为 \(j\), 且 \(i\) 在集合中的方案数.

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

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

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

\[f_{i, j \wedge a_i, 0} += g_j\\ f_{i, j \& a_i, 1} += g_j \]

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

#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, k \leq 20\)

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

\(dp_{i, state}\) 表示前 \(i\) 位, \(State\) 所表示的权值和能取得的方案总数.

枚举第 \(i + 1\) 个取的数 \(x\)

\[f_{i, (i | (i << x) | (1<<(x-1)))\ \&\ ((1<<K)-1)} += f_{i - 1, j}\\ f_{i + 1, (原来凑的 | 加上 x 新凑的 | x 本身的) \& (防止溢出)} += f_{i, j} \]

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

\[f_{i, j} += f_{i - 1, j} * (l - k) \]

\(O(n^22^n)\), 但是状压废状态比较多, 剪掉废状态即可 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

一个 \(n * n\) 的棋盘

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

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

\(0 < n \leq 100, 0 \leq m \leq 9, S_{num} \leq 5\)

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

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

设 \(f_{i, 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; 
}

标签:校内,NOIP,int,register,unsigned,rdch,key,return,模拟
来源: https://www.cnblogs.com/Wild-Donkey/p/14418996.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有