1. 程式人生 > 其它 >[NOIP2014 提高組] 題解

[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;
} 

嚶嚶嚶 為什麼一上考場就裂開 這和現在普及組水平應該差不多吧(

對了取膜那個膜不是打錯了

( ゚∀゚)o彡゜ ヒーコー ヒーコー!