noip2019…counting down three weeks
截止今日写完了从2014至2017的所有真题T1,
(也许某些不是T1…反正洛谷里边儿按着顺序就做了///)
虽说不敢保证每道题都百分百弄懂,但还是觉得有必要进行下小小的总结…
以便让自己知道这些天来自己都肝了些什么…
胡乱扯点什么…
(好像本来应该放在最后…)
- 感觉虽然每年的T1都不会很难,但还是会有些非常神奇非常因吹斯听的思想囊括其中的,~~(令本蒟蒻看着题解不禁大喊bravo!)~~比如神奇的幻方里移动位置的巧妙判断,玩具谜题里的巧妙转换,还有小凯的疑惑!!题目贼复杂但规律贼简单(虽然证明也很复杂…)!!虽然想通之后会觉得仿若送分题,但若是某一点点卡住了就…秒变送命题o( ̄︶ ̄)o…
- 从前写题写一题忘一题,现在写题…
“等等我这里到底懂了没懂了没啊啊啊啊一会博客里怎么写才让以后的那个睿智zwt看得懂啊啊啊啊” - 总之…发现总结其实还是挺重要的吧,至少能让自己发现哪些地方是真的懂了,哪些地方只是一知半解(虽然写在博客里也会有些东西是迷迷糊糊的…),但每次哪怕只是懂了个大体思路,也会觉得:
!!!!!!天啦撸我终终终终于弄懂了(一点点)!!!!!
……
五柳先生说的好哇!
*“信竞归来,识盈虚之有数”*╮(╯▽╰)╭
(确是如此,盈的是赘肉虚的是发量╭(╯^╰)╮)
还是开始正文题解吧…o(╥﹏╥)o
2014DAY 1_T1 生活大爆炸版石头剪刀布
(我也快大爆炸了(* ̄︶ ̄)…)
洛谷 1328 生活大爆炸版石头剪刀布
(题面往上找原题描述)
- 根据周期规律,直接读入每个人的出拳规律存到数组里,
遍历for过程中若超过周期na / nb则重置为1
题目数据较弱,可以直接列出ans++和bns++的情况,
在轮数范围内枚举比对,更新答案
AC代码
(虽然看着很弱但它能过(* ̄︶ ̄))
#include <bits/stdc++.h>
using namespace std;
int n, na, nb, x, y, xa[205], xb[205], zz, ans = 0, bns = 0;
int z = 0, w = 0;
int main()
{
cin >> n >> na >> nb;
for(int i = 1; i <= na; ++i)
{
cin >> xa[i];
}
for(int i = 1; i <= nb; ++i)
{
cin >> xb[i];
}
for(int a = 1; a <= n; ++a)
{
z++;
w++;
if(z > na)
z = 1;
if(w > nb)
w = 1;
if(xa[z] == 0 && (xb[w] == 2 || xb[w] == 3))
ans++;
if(xa[z] == 1 && (xb[w] == 0 || xb[w] == 3))
ans++;
if(xa[z] == 2 && (xb[w] == 1 || xb[w] == 4))
ans++;
if(xa[z] == 4 && (xb[w] == 0 || xb[w] == 1))
ans++;
if(xa[z] == 3 && (xb[w] == 2 || xb[w] == 4))
ans++;
if(xa[z] == 0 && (xb[w] == 1 || xb[w] == 4))
bns++;
if(xa[z] == 1 && (xb[w] == 2 || xb[w] == 4))
bns++;
if(xa[z] == 2 && (xb[w] == 0 || xb[w] == 3))
bns++;
if(xa[z] == 3 && (xb[w] == 0 || xb[w] == 1))
bns++;
if(xa[z] == 4 && (xb[w] == 2 || xb[w] == 3))
bns++;
}
cout << ans << " " << bns << endl;
return 0;
}
2014DAY 2_T1 无线网络发射器选址
洛谷 2038 无线网络发射器选址
(题面描述见链接)
太久以前写的题了…具体思路…不记得了…
还好当年有点自!知!之!明!代码有丢丢注释…
直接上AC代码:
思路部分来源洛谷题解
#include<bits/stdc++.h>
using namespace std;
struct zuob{
int num=0;
}s[130][130]; //定义一个结构体存储每个坐标的值
int d,n;
long long sum=0,ans=0,num=0,z=0,x1,x2,y2,ya;
int main()
{
cin>>d>>n;
for(int i=0;i<n;i++)
{
int a,b,k;
cin>>b>>a>>k; //a点和b点与二维数组的x,y相反要处理下 ??
s[a][b].num=k;
}
for(int i=0;i<=128;i++) //开始枚举
{
for(int j=0;j<=128;j++)
{
z=0; //先清空
if(i-d<0) x1=0; //判断纵坐标起点
else x1=i-d;
if(i+d>128) x2=128; //判断纵坐标终点
else x2=i+d;
if(j-d<0) ya=0; //判断横坐标起点
else ya=j-d;
if(j+d>128) y2=128; //判断横坐标终点
else y2=j+d;
for(int a=x1;a<=x2;a++) //对公共场所数量累加
for(int b=ya;b<=y2;b++)
z+=s[a][b].num;
if(z>sum) //如果大于前一个值则替换
{
sum=z;
num=1;
}
else if(sum==z) //如何相等则数量相加
num++;//num为方案数,同为最优解直接方案数+1
}
}
cout<<num<<" "<<sum; //输出
return 0;
}
- 看了下题解思路,大概可以选取直接模拟,或者是维护二维前缀和+递推
(注意某些边界情况的判断!!)
2015DAY 1_T1 神奇的幻方
- 注意位置变换方式,详见某篇带图题解…
- 往右上角移动填数,若右上角已经出界则掉到右上角该列 / 该行对应最里边儿,
(若没地方可掉则往自身正下方一格填数) - 若右上角已经填了数字,则往自身正下方一格填数
AC代码如下
注意位置转移的判断!
//2015DAY1_T1
//洛谷2615神奇的幻方
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 10001;
ll zz[maxn][maxn];
int n;
int sum = 1, z, c;
int main()
{
cin >> n;
z = 1, c = n / 2 + 1;
while(sum <= n * n)
{
zz[z][c] = sum;
if(sum % n == 0)
{
++z;
if(z == n + 1)
z = 1;
}
else
--z, ++c;
if(z == 0)
z = n;
if(c == n + 1)
c = 1;
++sum;
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
cout << zz[i][j] << " ";
cout << endl;
}
return 0;
}
2015DAY 2_T1 跳石头
洛谷 2678 跳石头
(题目描述见链接)
- (洛谷题解思路)
- 二分跳跃距离
把这个跳跃距离“认为”是最短的跳跃距离,然后去以这个距离为标准移石头。
使用一个judge判断这个解是不是可行解。
如果这个解是可行解,则有可能会有比这更优的解,去它的右边二分。
····为什么去右边?
答案是,这个区间是递增的 ,而我们求的是最短跳跃距离的最大值,
显然再右边的值肯定比左边大,那么我们就有可能找到比这更优的解,
直到找不到,那么最后找到的解就有理由认为是区间内最优解。
···反过来,如果二分到的这个解是一个非法解,我们就不可能再去右边找了。
因为性质,右边的解一定全都是非法解。那么我们就应该去左边找解。
整个过程看起来很像递归,实际上,这个过程可以递归写,
也可以写成非递归形式
直接上代码
#include <bits/stdc++.h>
using namespace std;
int d, n, m;
int a[500005], l, r, mid, ans;
inline int read()//快读优化
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
bool check(int x)
{
int tot = 0, i = 0, now = 0;
while(i < n + 1)
{
i++;
if(a[i] - a[now] < x)
tot++;
else
now = i;
}
if(tot > m)
return false;
else
return true;
}
int main()
{
d = read();
n = read();
m = read();
for(int i = 1; i <= n; ++i)
a[i] = read();
a[n + 1] = d;
l = 1, r = d;
while(l <= r)
{
mid = (l + r) >> 1;
if(check(mid))
{
ans = mid;
l = mid + 1;
}
else
r = mid - 1;
}
printf("%d", ans);
return 0;
}
2016DAY 1_T1 玩具谜题
洛谷 1563 玩具谜题
(乍一看十分有趣并且仔细看也十分有趣的“看图说话”题(* ̄︶ ̄)…呵)
- 拿0 or 1记录小人朝向内or外(朝向和job一起记在结构体里),再拿个数组记录向左还是向右走,
- 由于是个圈注意超过n的位置转换
直接上代码吧…
已AC
(但我反而找不到当初看的那篇题解了…大约是我看到的最简单的解法了///)
直接模拟+位置巧妙变换(#.#)
#include<bits/stdc++.h>
using namespace std;
struct node{
int pre;
string job;
}a[100005];
int n, m, direct, num;
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> a[i].pre >> a[i].job;
}
int now = 1;
//从1开始存的数据,起始位置为a[1]/ 若为0开始存的数据,则起始位置为a[0]
for(int i = 1; i <= m; i++)
{
cin >> direct >> num;
if(a[now].pre == 0 && direct == 0)
now = (now + n - num) % n;
else if(a[now].pre == 0 && direct == 1)
now = (now + num) % n;
else if(a[now].pre == 1 && direct == 0)
now = (now + num) % n;
else if(a[now].pre == 1 && direct == 1)
now = (now + n - num) % n;
if(now == 0)
now = n;
}
cout << a[now].job << endl;
return 0;
}
2016DAY 2_T1 组合数问题
有空可以再写写此题!!数论一定要学好!!o(╥﹏╥)o
··记得当时暑假跟着高一一起听数论的时候…最后考试好像还考到了这道题…
··然鹅当时一睿智就没注意数据范围…
··费马小定理(逆元法)只可以用来求模数为质数的情况!!!
··如果 n和m都不大,考虑递推 或者数组记录!!
··访问次数贼多贼多:先 用数组存好一定范围内的组合数,访问到直接O(1)输出
当初老师的AC代码…
吹爆!!
#include <cstdio>
#define N 2010
#define M 2000
using namespace std;
int T, mod;
int C[N][N], S[N][N], sum[N];
void Combination()
{
C[0][0] = 1;
for (int i = 1; i <= M; i++)
{
C[i][0] = 1, C[i][i] = 1;
for (int j = 1; j < i; j++)
{
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
sum[j] = sum[j - 1] + (C[i][j] == 0);
S[i][j] = S[i - 1][j] + sum[j];
}
for (int j = i; j <= M; j++)
{
sum[j] = sum[j - 1];
S[i][j] = S[i - 1][j] + sum[j];
}
}
}
int main()
{
scanf("%d%d", &T, &mod);
Combination();
while (T--)
{
int n, m;
scanf("%d%d", &n, &m);
printf("%d\n", S[n][m]);
}
return 0;
}
2017DAY 1_T1 小凯的疑惑
小婷的疑惑:出题人莫不是想要我死…
洛谷 3951 小凯的疑惑
- 可以打表找规律!!(相信直觉手算也ok!!)规律贼简单!!!
(证明贼复杂…得去翻我“当年”的笔记…还不一定懂)
还是上网找证明方法吧(* ̄︶ ̄)
上个AC代码吓死你╭(╯^╰)╮
#include<bits/stdc++.h>
using namespace std;
long long a, b;
int main()
{
cin >> a >> b;
cout << a*b-a-b <<endl;
return 0;
}
吓到没?!
吓到没?!
吓!到!没!?!
这么短的代码你见过么?!
(反正知道题解的那一刻我快哭了…┭┮﹏┭┮)
2017DAY 2_T1 奶酪
洛谷 3958 奶酪
(思路详见题解…(* ̄︶ ̄))
相切or 相交:两洞之间距离 <= 球的直径
判断一下洞与洞之间是否相切或相交(是否可以穿过),
并且对于是否为底层上的洞进行一个判断
写不出来了直接上代码吧…
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll r, n, h, check;
ll visit[1005];
struct hole
{
double x, y, z;
}hh[1005];
bool pand(hole a, hole b)
{
ll dis;
dis = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z);
if(dis <= 4 * r * r)
return true;
else
return false;
}
void doo(int x)
{
if(check == 1)
return;
if(hh[x].z + r >= h)
{
check = 1;
return;
}
for(int i = 1; i <= n; ++i)
{
if(visit[i] == 1)
continue;
if(pand(hh[x], hh[i]))
{
visit[i] = 1;
doo(i);
}
}
}
int main()
{
int c;
scanf("%d", &c);
for(int j = 1; j <= c; ++j)
{
scanf("%d%d%d", &n, &h, &r);
check = 0;
for(int i = 1; i <= n; ++i)
visit[i] = 0;
for(int i = 1; i <= n; ++i)
scanf("%lf%lf%lf", &hh[i].x, &hh[i].y, &hh[i].z);
for(int i = 1; i <= n; ++i)
if(hh[i].z <= r)
{
visit[i] = 1;
doo(i);
}
if(check == 1)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
18年的题还没敢开始看…o(╥﹏╥)o
机房一低头遍地都是我的头发:)
除了我头上,到处都是我头发:)
本文深入解析了NOIP2014至2017年竞赛真题,覆盖了从算法设计到数据结构应用的多个方面,包括石头剪刀布、无线网络选址、幻方构造、跳石头策略、玩具谜题解决、组合数问题、小凯的疑惑解答及奶酪问题分析。

692

被折叠的 条评论
为什么被折叠?



