前言:本次考试第二题炸了,删了暴力,后果很惨。。。


a 约数

题目描述

设K是一个正整数,设X是K的约数,且X不等于1也不等于K.
加了X后,K的值就变大了,你可以重复上面的步骤。例如K= 4,我们可以用上面的规则产生所有的非素数. 可以通过5次变化得到
24: 4->6->8->12->18->24.
现在给你两个整数N 和 M, 求最少需要多少次变化才能到从 N 变到 M. 如果没法从N变到M,输出-1.


输入格式

多组测试数据。
第一行:一个整数1<=ng<=5,表示有ng组测试数据。
每组测试数据格式如下:
一行:两个整数,N、M,空格分开。 4 <= N<=100000, N<=M<=100000.


输出格式

一个整数。求最少需要多少次变化才能到从 N 变到 M. 如果没法从N变到M,输出-1.
ng行,每行对应一组测试数据。


输入样例

2
4 24
4 576


输出样例

5 (题目的例子)
14(4->6->8->12->18->27->36->54->81->108->162->243->324->432->576)


解题思路(bfs)

这题这一看貌似数论,吓得我赶紧手推了半天,无果,发现N,M才100000而已,赶紧写了个bfs一次AC此水题。
看到签到题果真不能想太多,不要将简单的问题复杂化,而应将复杂的问题分解,使之简单化。
时间 O(ng∗n∗n√) O(ng*n*\sqrt{n})。


代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#define N 100005using namespace std;int ng, n, m;
int head, tail;
int q[N], step[N];int main(){freopen("a.in", "r", stdin);freopen("a.out", "w", stdout);scanf("%d", &ng);while(ng --){scanf("%d%d", &n, &m);for(int i = 1; i <= m; i++)  step[i] = -1;q[head = tail = 0] = n;step[n] = 0;while(head <= tail){int now = q[head++];for(int i = 2; i * i <= now; i++){if(now % i != 0)  continue;int next = now + i;if(next <= m && step[next] == -1){q[++tail] = next;step[next] = step[now] + 1;}if(i != now / i){next = now + now / i;if(next <= m && step[next] == -1){q[++tail] = next;step[next] = step[now] + 1;}}}}printf("%d\n", step[m]);}return 0;
}

b 小偷与警察

题目描述

为帮助捕获在逃的犯人, 警局引进了一套新计算机系统. 系统覆盖了N 个城市,有E条双向的道路。城市标号为1 到N. 犯人经常从一个城市逃到另外一个城市. 所以警察想知道应该在哪里设置障碍去抓犯人.计算机系统需要回答下面两种类型的问题:
1. 考虑城市A 、B; 如果把连接城市G1和G2的那条公路切断,逃犯还能从城市A逃到城市B吗?
2. 考虑三个城市A、B 、C. 如果把城市C封锁(则不能从其他进入城市C),逃犯还能从城市A逃到城市B吗?
你的任务是帮计算机系统回答这些提问。(一开始,任意两个城市都是可以相互到达的).


输入格式

  • 第一行: 两个整数N 、 E (2 <= N <= 100 000, 1 <= E <= 500 000),表示城市的数量和道路的数量.
  • 第 2..E+1行: 两个不同整数A和B,表示城市A和城市B之间有一条公路,任意两个城市最多只有一条公路。
  • 第 E+2行:一个整数 Q (1 <= Q <= 300 000), 表示有Q个提问。
  • 第 E+3..E+Q+2行: 这Q行,每行有4个或5个整数. 第一整数表示提问的类型(1或2). 如果是提问类型是1, 那么本行后面有4个整数: A、B、G1、G2 ,参数表示的意义题目已经说过,其中A和B不会相同,G1和G2之间肯定有一条公路. 如果提问类型是2,那么本行后面有3个整数: A、B 、C. ( A、B 、C都不相同,意义上面已经说过。)

输出格式

  • 第1..Q行: 每行输出yes 或 no .

输入样例

13 15
1 2
2 3
3 5
2 4
4 6
2 6
1 4
1 7
7 8
7 9
7 10
8 11
8 12
9 12
12 13
5
1 5 13 1 2
1 6 2 1 4
1 13 6 7 8
2 13 6 7
2 13 6 8


输出样例

yes
yes
yes
no
yes


解题思路(dfs树+LCA(树上倍增))

这是一道好题。考试时乱写了个Tarjan+LCA,没有考虑到割顶的情况,就爆9了。都怪我写了暴力又删了,哪来的自信啊!
然后我就发现这题跟割顶有点关系。

(以下皆为口胡)
割顶の定义:割掉一个点 A A后,图连通分量个数增加,则AA为割顶。
割顶の性质:记点 i i的dfs序为dfn[i]dfn[i], low[i] low[i]为从点 i i通过一条返祖边连回的最早的dfndfn。若 A A为割顶,当且仅当其存在一个儿子vv使得 low[v]>=dfn[A] low[v]>=dfn[A]。
割顶の求法:
①枚举每个点删掉,统计连通分量个数。
②据性质线性求(根处要特判)

桥の定义:割掉一条边 L L后,图连通分量个数增加,则LL为桥。
桥の性质:记深度为 dep[i] dep[i],若边 A−B A-B为桥( dep[A]<dep[B] dep[A]),当且仅当 low[B]>dfn[A] low[B]>dfn[A]。类比割顶,我们发现桥的上方必然有一个割顶。换而言之,存在割顶是其下方存在桥的必要条件。
桥の求法:类比割顶。

我们考虑先对此无向图做一遍dfs,求得其dfs树。由于无向图,我们只有树边返祖边。如果是有向图的dfs树是有树边返祖边横叉边正向边的。
我一开始是想着求双联通分量然后什么缩环之类的,其实并不用。

考虑割一个点,如果在起点与终点的路径上的话(求LCA),才可能有影响。然后此点必然是割顶(必要条件)。然后用树上倍增从一个点跳过去其下方的节点,看看能不能跑掉(即通过返祖边)。这个用类似于求割顶的方法(图我就不画了)。
然后这里有一个特例,如果要割的点在LCA的话,要两边都跳以下,一端被割就不行了。单次时间 O(log n) O(log\ {n})。(ps:这个地方我错了好久QAQ)

考虑割一条边,这个简单多了,比上一个好想。直接判断边在路径上吗,不在一定无影响。否则判断是否为桥即可。

这题亦可不用LCA用(LCT×)。我们发现能否连通跟两点是否在一棵子树有关系,于是直接利用dfs序判断就可以。这就不用LCA了(其实也一样)。然后判断割点时还是要树上倍增一下,或者二分+链表(vector),并没有好写多少。


代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define N 100010
#define M 500010using namespace std;int n, m, Q, cur, Tim, child;
int head_p[N];
int dep[N], dfn[N], low[N];
int f[25][N];
struct Tadj{int next, obj;} Edg[M<<1];void Init(){cur = -1;memset(head_p, -1, sizeof(head_p));memset(dfn, 0, sizeof(dfn));
}void Insert(int a, int b){cur ++;Edg[cur].next = head_p[a];Edg[cur].obj = b;head_p[a] = cur;
}void dfs(int root, int fa){dfn[root] = low[root] = ++ Tim; dep[root] = dep[fa] + 1;f[0][root] = fa;for(int i = head_p[root]; ~ i; i = Edg[i].next){int v = Edg[i].obj;if(!dfn[v]){if(root == 1)  child ++;dfs(v, root);low[root] = min(low[root], low[v]);}else if(dfn[v] < dfn[root] && v != fa)  low[root] = min(low[root], dfn[v]);}
}void ycl(){dfs(1, 0);for(int i = 1; i <= 20; i++)for(int j = 1; j <= n; j++)f[i][j] = f[i-1][f[i-1][j]];
}int Getlca(int x, int y){if(dep[x] > dep[y])  swap(x, y);for(int i = 20; i >= 0; i--)if(dep[f[i][y]] >= dep[x])  y = f[i][y];if(x == y)  return x;for(int i = 20; i >= 0; i--)if(f[i][x] != f[i][y]){x = f[i][x];y = f[i][y];}return f[0][x];
}bool Onpath(int A, int B, int C){int L1 = Getlca(A, B), L2 = Getlca(A, C), L3 = Getlca(B, C);if(dfn[C] < dfn[L1])  return false;if(L2 != C && L3 != C)  return false;return true;
}void Jump(int A, int B, int C){int L1 = Getlca(A, B), L2 = Getlca(A, C), L3 = Getlca(B, C);int y;if(L2 == C){y = A;for(int i = 20; i >= 0; i--)if(dep[f[i][y]] > dep[C])  y = f[i][y];if(!(low[y] < dfn[C] || (C == 1 && child == 1))){  printf("no\n");return;}}if(L3 == C){y = B;for(int i = 20; i >= 0; i--)if(dep[f[i][y]] > dep[C])  y = f[i][y];if(!(low[y] < dfn[C] || (C == 1 && child == 1))){  printf("no\n");return;}}printf("yes\n");
}int main(){freopen("b.in", "r", stdin);freopen("b.out", "w", stdout);scanf("%d%d", &n, &m);Init();int a, b;for(int i = 1; i <= m; i++){scanf("%d%d", &a, &b);Insert(a, b);Insert(b, a);}ycl();  scanf("%d", &Q);int sign, A, B, C, G1, G2;for(int i = 1; i <= Q; i++){scanf("%d", &sign);if(sign == 1){scanf("%d%d%d%d", &A, &B, &G1, &G2);if(!Onpath(A, B, G1) || !Onpath(A, B, G2))  printf("yes\n");else{if(dfn[G1] > dfn[G2])  swap(G1, G2);if(low[G2] > dfn[G1])  printf("no\n");else  printf("yes\n");}}else{scanf("%d%d%d", &A, &B, &C);if(!Onpath(A, B, C))  printf("yes\n");else  Jump(A, B, C);}}return 0;
}

c 圆桌会议

题目描述

有N个人顺时针围在一圆桌上开会,他们对身高很敏感. 因此决定想使得任意相邻的两人的身高差距最大值最小. 如果答案不唯一,输出字典序最小的排列,指的是身高的排列.


输入格式

多组测试数据。第一行:一个整数ng, 1 <= ng <= 5. 表示有ng组测试数据。
每组测试数据格式如下:
第一行: 一个整数N, 3 <= N <= 50
第二行, 有个N整数, 第i个整数表示第i个人的身高hi, 1<=hi<=1000. 按顺指针给出N个人的身高. 空格分开。


输出格式

字典序最小的身高序列,同时满足相邻的两人的身高差距最大值最小。共ng行,每行对应一组输入数据。


输入样例

2
5
1 3 4 5 7
4
1 2 3 4


输出样例

1 3 5 7 4
1 2 4 3


解题思路(二分+(贪心/网络流)/双路dp)

排序后很明显的二分,然后套个贪心乱搞。
很明显答案一定是个单峰,类似于上一座山又下来。
于是确定最小值和最大值,之间就有两条路径。
为了能使二分到的答案满足,贪心地让上山经过的节点尽可能少,多的留给下山才可能从山峰走下。这里判一下能否登顶并下去。
然后得到答案后,考虑字典序问题。由于我们二分时是使山顶前的节点尽可能少,于是山峰尽可能靠前。而要求字典序最小要求山峰尽可能靠后。于是在二分时保存答案最后倒序输出就行了。
至于贪心的严谨证明,就是个麻烦活了。但我们还是能够理解并举几个例子检验的。提高贪心的证明能力需要提高数学证明的功底,推翻贪心需要能举出反例的脑洞。

然后深入思考,为什么选最后下山的路径不是从山顶尽快下去而是从山底尽快上去呢?这是不同的。因为从山顶尽快下去是遇到节点就选,然后到达最后,这与从山顶相比是将大的值放在了前面,明显我们要是字典序小就要将大的值放后面。所以这个从先上山变成后下山的想法正确的。换而言之,我们无法交换两条路径上的值使答案更优,而一条路上的值也不能单独改变,所以这是有道理的。
说一句废话:都已AC的贪心你不能说它哪里不对吧。。。

另两种方法:二分后用网络流检验,看看能否跑出两条路径就行了。重点在与字典序最小,就枚举看看每个点能不能放,再合并点跑网络流检验就行了。很强大的一种方法。

双路dp就不用二分,直接记状态(记忆化搜索),然后记抛物线的开口下的两条支路的结尾的答案为 f[i][j] f[i][j],然后枚举下一个点放哪里转移。然后得到答案,字典序也用了跟算答案有关的方法。直接枚举第 i <script type="math/tex" id="MathJax-Element-20">i</script>个是谁,然后查表看看满不满足,还有其他细节的话我也不清楚。
其实算答案直接将第一小放左边,第二小放右边,累次放下去一定最优。然后各种方法调整字典序(交换之类,或者直接贪心地放)就可以得到答案。

总之二分+贪心就够好了。所有方法调整字典序的方法都是与算答案的方法类似。


代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define oo 0x7fffffff
#define N 60using namespace std;int ng, n, last, h[N];
bool vis[N], rec[N];bool Judge(int dis){for(int i = 1; i <= n; i++)  vis[i] = false;last = 1;  vis[1] = true;for(int i = 2; i <= n; i++)if(h[i] - h[last] > dis){  last = i - 1;if(vis[last])  break;vis[last] = true;}if(h[n] - h[last] > dis)  return false;vis[1] = vis[n] = false;for(int i = 1; i < n; i++)for(int j = i+1; j <= n; j++){if(vis[i] || vis[j])  continue;if(h[j] - h[i] > dis)  return false;break;}for(int i = 1; i <= n; i++)  rec[i] = vis[i];return true;
}int main(){freopen("c.in", "r", stdin);freopen("c.out", "w", stdout);scanf("%d", &ng);while(ng --){scanf("%d", &n);for(int i = 1; i <= n; i++)  scanf("%d", &h[i]);sort(h+1, h+n+1);int L = -1, R = h[n] - h[1];while(L + 1 < R){int mid = (L + R) >> 1;if(Judge(mid))  R = mid;else  L = mid;}if(!Judge(R))  printf("My hope is the world peace");for(int i = 1; i <= n; i++)  if(!rec[i])  printf("%d ", h[i]);for(int i = n; i > 0; i--)  if(rec[i])  printf("%d ", h[i]);printf("\n");}return 0;
}

总结

提高码代码的准确度与稳定性,思考时间不能少,要学会水分和暴力。
还能说什么呢,加油吧,没有天赋,到达终点只能靠努力。
但求无悔。


还会有人让你睡不着,还能为某人燃烧 。

NOIP2017模拟赛(4) 总结相关推荐

  1. NOIP2017模拟赛总结(2017.10.30-2017.11.1)

    第三篇博客,放上2017.10.30-2017.11.1的题. 2017.10.30 Problem A 题目大意: 有一排nnn棵果树和一个容量为sss的果篮,从前往后摘果,如果当前果树的果子数量不 ...

  2. 清北学堂 NOIP2017模拟赛 越赛越心塞

    连续考了一个星期发现自己真的是手感型选手,成绩全靠天意.手感好了码出200+也没什么问题,推出式子并且打出自己都不信的操作也有过.手感差了......就一个呵呵二字. 然后开始是T总让我们休息了一个星 ...

  3. EZ 2018 06 17 NOIP2018 模拟赛(十九)

    这次的题目难得的水,但是由于许多哲学的原因,第二题题意表述很迷. 然后是真的猜题意了搞了. 不过这样都可以涨Rating我也是服了. Upt:链接莫名又消失了 A. 「NOIP2017模拟赛11.03 ...

  4. NOIP2017提高组模拟赛4 (总结)

    NOIP2017提高组模拟赛4 (总结) 第一题 约数 设K是一个正整数,设X是K的约数,且X不等于1也不等于K. 加了X后,K的值就变大了,你可以重复上面的步骤.例如K= 4,我们可以用上面的规则产 ...

  5. 20190405 DP模拟赛1总结

    20190405 DP模拟赛1总结 概况 重要的模型&&方法:T1,T2,T3 分数 失分原因 题目及其题解 [ T1:Lg P2737 [USACO4.1]麦香牛块Beef McNu ...

  6. NOI.AC NOIP模拟赛 第六场 游记

    NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...

  7. 2017.6.11 校内模拟赛

    题面及数据及std(有本人的也有原来的) :2017.6.11 校内模拟赛 T1 自己在纸上模拟一下后就会发现 可以用栈来搞一搞事情 受了上次zsq 讲的双栈排序的启发.. 具体就是将原盘子大小cop ...

  8. 2020年蓝桥杯模拟赛2020.3.25直播笔记

    2020年蓝桥杯模拟赛解题报告(CPP版本) 第八题 长草的bfs写法[我想暴力模拟O kmn] 深搜会爆 bfs像投到水里的涟漪 问题: const int dx[] = {1, 0, -1, 0} ...

  9. 2021年 第12届 蓝桥杯 第4次模拟赛真题详解及小结【Java版】

    蓝桥杯 Java B组 省赛决赛 真题详解及小结汇总[2013年(第4届)~2021年(第12届)] 第11届 蓝桥杯-第1.2次模拟(软件类)真题-(2020年3月.4月)-官方讲解视频 说明:大部 ...

最新文章

  1. 针对web服务器容灾自动切换方案
  2. Python代码加密混淆
  3. 请输入星期的第一个字母c语言,C语言经典案例:请输入星期几的第一个字母来判断一下是星期几,...
  4. 分布式设计与开发(四)------数据拆分
  5. python基本运算_python中的基本运算
  6. java String补足
  7. PHP学习笔记:利用gd库给图片打图片水印
  8. JavaScript计算器
  9. Paper-----文献引用格式
  10. 小白攻略(三):数学建模论文的写作
  11. 《基于MFC的OpenGL编程》Part 7 Animation
  12. 巴旦木和杏仁的营养价值哪个好?丨巴旦木功效与作用
  13. win10右键卡顿原因_如何解决Win10桌面右键一直卡顿转圈的问题?
  14. uniapp——ios端和android端微信分享,通过打开appStore和应用宝商店下载
  15. 裸辞闹了个乌龙,就当做个题提升自己吧
  16. 【常见问题】error LNK2005: 函数已经在某文件中定义
  17. 国内的智能家居品牌有哪些
  18. c语言霍夫变换圆检测,Hough Transform(霍夫变换)检测Circle(圆)的几种方法
  19. ImageView中动态设置图片
  20. 文献科普|DNA甲基化通过CTCF和黏着蛋白复合物调节选择性聚腺苷酸化

热门文章

  1. ABAP LVC DEMO程序
  2. 18-19款迈巴赫S400改装原厂3D大柏林之声,天籁之音
  3. 利用NCBIdatasets批量下载大规模生信数据集
  4. SCI,SSCI,EI傻傻分不清
  5. Unity刘海屏幕适配
  6. springmvc自定义参数解析器
  7. selenium下拉列表定位之 select+option 的定位
  8. Java小程序 个人缴税
  9. 横河变送器EJA430E-JCS4G-917DB
  10. Photo Album: 2008年5月-三亚爱琴海岸康年度假村-day4