NOIP 2017 游记
这次NOIP,总的来说发挥还不错,能做起的题基本上没有丢分,但遗憾的是D2T3没有骗到足够多的分,思想没有清晰的时候就去急急匆匆写正解,最后正解调不出来沦为暴力选手。遗憾遗憾。。。
Day 1
- T-1:
一眼的公式题,没什么好说的。
输出 \(a*b-a-b\) 即可,记得开 \(long\ long\) 。
- T-2:
大模拟,手工模拟栈即可。
写起来有点烦,各种情况需要分类讨论。
关于复杂度:
-
\(x\in N^*,y\in N^*\)
\(x\le y\) -> \(O(1)\)
\(x\gt y\) -> 以后均不执行
-
\(x\in N^*,y=n\)
以后均不执行
-
\(x=n,y\in N^*\)
-> \(O(n)\)
-
\(x=n,y=n\)
-> \(O(1)\)
关于语法错误:
- 每次字母判重
- 退栈时检查是否栈已经为空
- 结束后查看栈中是否仍有元素
可以考虑标记一下语法错误,若当前程序已出现语法错误,以后的便只输入,不进行变动。注意每组数据要记得清空。
输入的话可以考虑先读入一个字符数组,再使用\(sscanf\),比较方便。
scanf("%s",buf); sscanf(buf,"O(%d)",&w);
- T-3:
最短路计数之类的。
题目中\(K\)值给的很小,可以考虑DP。
令状态\(f(i,d)\),表示当前在 \(i\) 号节点,已走距离为 \(dis(1,i)+d\) 的方法总数。最后的答案便是 \(\Sigma f(n,j)\) ,其中 \(j\) 取遍 \(0\)~\(K\)。
后面的转移非常好想。
\(f(i,d)\) -> \(f(v,len(i,j)+dis(1,i)+d-dis(1,j))\)
\(j\) 是与 \(i\) 相连的节点
先从\(f(1,0)\)出发,宽搜寻找可行状态,重新建图连边。
(这样处理有点慢,复杂度\(O((n+m)*K)\),但当时我就是这样做的
(判可行状态不仅要判\(d\le K\),还要判
\(dis(1,i)+dis(i,n)+d\le dis(1,n)+K\)
否则状态过多,常数过大。。。
后面直接 \(Topsort\) 即可。
零环的处理直接看 \(Topsort\) 后 \(n\) 号节点的入度是否为 \(0\) 即可。
Day 2
-
T-1:
连边判连通性。
注意考虑边界情况。
可能爆 \(long\ long\) 。
并查集: \(O(n^2+\alpha(n)*n)\)
宽搜: \(O(n^2)\)
-
T-2:
我只想到了\(O(n^3 3^n)\)的DP。
状态: \(f(s,i,d)\),其中 \(s\) 为当前的树中的节点集合,\(i\) 为当前树的根,\(d\) 为 \(i\) 在最终的树里的深度。
转移非常好想:
\(f(s,i,d)=min\{f(t,j,d+1)+len(i,j)*(d+1)\}\)
其中 \(t\) 是 \(s\) 的子集,\(j\) 是 \(t\) 那颗树的根。
这样做时间非常卡,很可能超时。
于是常数优化hiahiahia~~
\(i,j\) 的话利用 \(lowbit\) 枚举,\(d\) 是有范围的,对于给定\(s\),\(d\le n-card(s)\)。
以上。应该可以卡过。
- T-3
当时没有写出来,实际上下来想想,就是一道动态开点的线段树。。。
假设我们要找到某一行中的第 \(x\) 个人。
我们不妨设有一个序列,每个位置有一个值,没出队为1,出队了为0,显然,我们只需要找第一个前缀和为 \(x\) 的位置。
对于线段树来说,这十分容易。
int findkth(int k,int p,int l,int r) { if(l==r) return l; int mid=l+r>>1; if(sum[ls]<k) return findkth(k-sum[ls],rs,mid+1,r); else return findkth(k,ls,l,mid); }
由于有大量的节点实际上是不会用到的,我们动态开点即可。
新的元素可以用 \(vector\) 储存。
(然而考试时候脑子抽了主席树。。。高级数据结构学傻了,半天调不出来,最后只有交暴力。。。本来可以60分的 \(x=1\) 的情况,也抽风没有写对。。。过于遗憾)
综上,NOIP 2017 对于我来说还是有点遗憾的,希望CCF老爷机不会太卡,能暴力上500就好啦。能在弱省学OI感觉还是很幸运,假如生在ZJ的话就只有当(渣渣?)之类的了吧。。。