[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 提高组] 寻找道路
挺简单的吧..
- 反向建边, 从终点开始 dfs 看哪些点能到达终点
- 枚举每个点的每个子节点看这个点是否满足
出边所指向的点都直接或间接与终点连通
- 用最短路, 跳过不满足题意的节点
#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彡゜ ヒーコー ヒーコー!