[NOIP2014 提高组] 题解

一个下午做了个 530.. 没写过数论题所以 D2T3 暴力了个 30, 话说 AHOI 的时候也是忘记取膜 100 -> 20.. 为什么连同余都不会awa

也只有 14 年的提高组有会做的.. 最难的也不过蓝题+ \({\ }\) 反正我弱弱(
话说这不就是普及组摸你题(
难道我普及 400 都没有吗 哭哭

D1T1 P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布

打表模拟即可. 这都不会还是去打普及组吧.

#include <stdio.h>

const int score[5][5] = {
	{ 0, 0, 1, 1, 0 },
	{ 1, 0, 0, 1, 0 },
	{ 0, 1, 0, 0, 1 },
	{ 0, 0, 1, 0, 1 },
	{ 1, 1, 0, 0, 0 }
};

int n, n1, n2;
int a[203], b[203];

int main () {
	scanf("%d %d %d", &n, &n1, &n2);
	for (int i = 0; i < n1; i++)
		scanf("%d", &a[i]);
	for (int i = 0; i < n2; i++)
		scanf("%d", &b[i]);
	int ans1 = 0, ans2 = 0;
	for (int i = 0; i < n; i++) {
		ans1 += score[a[i % n1]][b[i % n2]];
		ans2 += score[b[i % n2]][a[i % n1]];
	}
	printf("%d %d\n", ans1, ans2);
	return 0;
}

D1T2 P1351 [NOIP2014 提高组] 联合权值

wqy 巨佬的 blog 讲的很清楚! 和我的想法完全相同. 去看看!!

认真读题仔细想想就行, 或者像我一样摸你赛时先打暴力然后数学推公式也能想出来! 多试试就行了(

核心公式:
\(2a_1a_2+2a_1a_3+...+2a_{n-1}*a_n = (a_1+a_2+...+a_n)^2 - (a_1^2+a_2^2+...+a_n^2)\)

#include <stdio.h>
#include <vector>
#include <algorithm>
#define ci const int &

int n;
int a[200003];
std:: vector <int> g[200003];
int ans1 = 0, ans2 = 0; 

inline void add_edge (ci x, ci y) {
	g[x].push_back(y);
	return;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		add_edge(x, y);
		add_edge(y, x);
	}
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	for (int i = 1; i <= n; i++) {
		int max1 = 0, max2 = 0, sum = 0, msum = 0;
		for (int j = 0; j < g[i].size(); j++) {
			int x = a[g[i][j]];
			if (x > max1)
				max2 = max1, max1 = x;
			else if (x > max2)
				max2 = x;
			sum = (sum + x) % 10007;
			msum = (msum + x * x) % 10007;
		}
		ans1 = std:: max(ans1, max1 * max2);
		if (g[i].size() >= 2)
			ans2 = (ans2 + sum * sum - msum) % 10007;
	}
	printf("%d %d\n", ans1, ans2);
	return 0;
}

D1T3 P1941 [NOIP2014 提高组] 飞扬的小鸟

很好的一道 DP 题, 转化为背包就行了(

#include <stdio.h>
#include <algorithm>

int n, m, q;
int x[10003], y[10003];
int low[10003], high[10003];	// 区间 [low[i],high[i]] 可以通过
bool iswall[10003]; 
int d[10003][1003]; 

int main() {
	scanf("%d %d %d", &n, &m, &q);
	for (int i = 1; i <= n; i++)
		low[i] = 1, high[i] = m;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= m; j++)
			d[i][j] = 0x3F3F3F3F; 
	d[0][0] = 0x3F3F3F3F;
	for (int i = 0; i < n; i++)
		scanf("%d %d", &x[i], &y[i]);
	for (int i = 1; i <= q; i++) {
		int a, b, c;
		scanf("%d %d %d", &a, &b, &c);
		low[a] = b + 1;
		high[a] = c - 1;
		iswall[a] = true;
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) 
			if (j - x[i - 1] >= 0) {
				d[i][j] = std:: min(d[i][j], d[i - 1][j - x[i - 1]] + 1);
				d[i][j] = std:: min(d[i][j], d[i][j - x[i - 1]] + 1);
			}
		for (int k = m - x[i - 1]; k <= m; k++) {
			d[i][m] = std:: min(d[i][m], d[i - 1][k] + 1);
			d[i][m] = std:: min(d[i][m], d[i][k] + 1);
		}
		for (int j = low[i]; j <= high[i]; j++)
			if (j + y[i - 1] <= m)
				d[i][j] = std:: min(d[i][j], d[i - 1][j + y[i - 1]]); 
		for (int j = 1; j <= m; j++)
			if (!(low[i] <= j && j <= high[i]))
				d[i][j] = 0x3F3F3F3F;
	}
	int ans1 = 0, ans2 = 0x3F3F3F3F;
	for (int j = 1; j <= m; j++)
		if (d[n][j] < ans2)
			ans2 = d[n][j], ans1 = 1;
	if (ans1 == 0) {
		ans2 = 0;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++)
				if (d[i][j] != 0x3F3F3F3F && iswall[i]) {
					ans2++;
					break;
				}
	}
	printf("%d\n%d\n", ans1, ans2);
	return 0;
}

D2T1 P2038 [NOIP2014 提高组] 无线网络发射器选址

枚举每个点能否成为最大答案, 时间复杂度 \(O(n^4)\)

虽然用上二维前缀和会更快 不过这样就能过了(

#include <stdio.h>
#include <algorithm>

int d, k, n = 128, m = 128;
int a[203][203];
int ans1 = 0, ans2 = 0;

int main() {
	scanf("%d %d", &d, &k);
	for (int i = 1; i <= k; i++) {
		int x, y, z;
		scanf("%d %d %d", &x, &y, &z);
		a[x][y] = z;
	}
	for (register int i = 0; i <= n; i++) {
		for (register int j = 0; j <= m; j++) {
			register int sum = 0;
			for (register int x = std:: max(0, i - d); x <= std:: min(n, i + d); x++)
				for (register int y = std:: max(0, j - d); y <= std:: min(m, j + d); y++)
					sum += a[x][y];
			if (sum == ans2)
				ans1++;
			else if (sum > ans2) {
				ans2 = sum;
				ans1 = 1;
			}
		}
	}
	printf("%d %d\n", ans1, ans2);
	return 0;
}

D2T2 P2296 [NOIP2014 提高组] 寻找道路

挺简单的吧..

  1. 反向建边, 从终点开始 dfs 看哪些点能到达终点
  2. 枚举每个点的每个子节点看这个点是否满足 出边所指向的点都直接或间接与终点连通
  3. 用最短路, 跳过不满足题意的节点
#include <stdio.h>
#include <vector>
#include <queue>
#define ci const int &

struct NODE {
	int p, val;
	inline const bool operator < (const NODE &rhs) const {
		return val > rhs.val;
	}
} nd;

int n, m;
int s, t;
std:: vector <int> g[2][10003];
int d[10003];
bool vis[10003];
bool cvis[10003];		// 出边所指向的点都直接或间接与终点连通的点 
bool ccvis[10003];		// 与终点相连的点 

inline void add_edge (ci x, ci y, ci p) {
	if (x == y)	// 自环 
		return;
	for (int i = 0; i < g[p][x].size(); i++)
		if (g[p][x][i] == y)	// 重边 
			return;
	g[p][x].push_back(y);
	return;
}

void dfs1 (int cur) {		// 从终点出发, 看哪些点能够到达终点 
	ccvis[cur] = true;
	for (int i = 0; i < g[1][cur].size(); i++) 
		if (!ccvis[g[1][cur][i]])
			dfs1(g[1][cur][i]);
	return;
}

inline void djs (int cur) {	// 最短路 
	for (int i = 1; i <= n; i++)
		d[i] = 0x3F3F3F3F;
	std:: priority_queue <NODE> pq;
	nd.p = cur, nd.val = 0;
	pq.push(nd);
	d[cur] = 0;
	while (!pq.empty()) {
		nd = pq.top(); pq.pop();
		int x = nd.p, val = nd.val;
		vis[x] = true;
		for (int i = 0; i < g[0][x].size(); i++) {
			int fw = g[0][x][i];
			if (!vis[fw] && cvis[fw] && d[fw] > val + 1) {
				d[fw] = val + 1;
				nd.p = fw, nd.val = d[fw]; pq.push(nd);
			}
		}
	}
	return;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		add_edge(x, y, 0);
		add_edge(y, x, 1);
	}
	scanf("%d %d", &s, &t);
	dfs1(t);
	for (int i = 1; i <= n; i++) {
		cvis[i] = true;
		if (ccvis[i] == false)
			cvis[i] = false; 
		for (int j = 0; j < g[0][i].size(); j++)
			if (ccvis[g[0][i][j]] == false)
				cvis[i] = false;
	}
	djs(s);
	printf("%d\n", d[t] == 0x3F3F3F3F ? -1 : d[t]);
	return 0;
}

D2T3 P2312 [NOIP2014 提高组] 解方程

30 分暴力应该比较好拿 用上快速幂时间复杂度为 \(O(mn\log{n})\) 再加上优化只要输入的数较小就能拿 80+ (应该能优化到 100)

然后我就不会了

看了眼题解发现对所有数进行取膜就行了, 输入的时候像快读一样再加上取膜就行了

顺便有个 秦九韶公式 可以把时间复杂度降低为 \(O(mn)\)

建议膜上 \(1e9+7\), 至于为什么自己 google

#include <stdio.h>
#include <ctype.h>
#define ll long long

const ll MODN = 1e9 + 7;

int n, m;
ll a[103];
int ans = 0, anss[1000003];
char ch;

inline ll scan () {
	ll x = 0ll, f = 1ll;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			f = -1ll;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = ((x << 1) % MODN + (x << 3) % MODN + ch - '0') % MODN;
		ch = getchar();
	}
	return x * f;
}

int main() {
	scanf("%d %d\n", &n, &m);
	for (int i = 0; i <= n; i++)
		a[i] = scan();
	for (register ll i = 1ll; i <= m; i++) {
		ll sum = 0ll;
		for (register int j = n; j >= 1; j--) 
			sum = (sum + a[j]) * i % MODN;
		sum = (sum + a[0]) % MODN;
		if (sum == 0ll)
			anss[++ans] = i;
	} 
	printf("%d\n", ans);
	for (int i = 1; i <= ans; i++)
		printf("%d\n", anss[i]);
	return 0;
} 

嘤嘤嘤 为什么一上考场就裂开 这和现在普及组水平应该差不多吧(

对了取膜那个膜不是打错了

posted @ 2021-08-13 21:05  dbg_8  阅读(43)  评论(0编辑  收藏  举报