欢迎使用CSDN-markdown编辑器

来源:互联网 发布:linux系统哪个版本好 编辑:程序博客网 时间:2024/06/04 23:31
  • 这次NOIP考完后原地爆炸.
    Day1
    心态直接爆炸,第二题模拟打了两个多小时最后却错了,心态原地爆炸,时间又不多了,结果其他的题目都没来得及认真思考
    * T1:
    就是一个简单的数学(zhao)归(gui)纳(lv),但是当时第一眼直接懵逼,woc我怎么从来没有见过这种第一题?
    然后一看数据范围,绝对是找规律,然后找了一下觉得可能没有这么简单就去找方法最后没找到
    (真是后悔当时没有坚持找下去,其实规律很简单)
    ** 方法
    输出两个数的积减去它们的和
    * T2:
    一个模拟,考完感觉十分变态,而且我智障的当时没有想到用栈。
    ** 方法
    就按照题目说的模拟就行了(用一个栈)
    * T3:
    这题要用到DP加最短路加判环,本来就DP不好,在加上当时没有时间了,连分都没有骗到。
    ** 方法
    题意:求1~n路径长度<=1~n最短路+K的路径数
    首先你可以考虑到Day1的DP去哪里了?
    没错,你只要再注意一下K<=50就大概能想到这是一个与k有关的DP了
    ①:考虑30pts:K=0 右转P1608路径统计(P1144最短路计数可以顺带A掉)
    ②:考虑70pts:没有0边
    考虑f[u][j]表示1->u,比dis1[u] (1~u的最短路)多j的路径数
    那么对于u->v长为w的边
    那么从1->u->v这条路径的长度就是dis1[u]+j+w-dis1[v]
    如果dis1[u]+w-dis1[v]+j<=K
    就可以从f[u][j]转移到f[v][dis1[u]+j+w-dis1[v]]
    所以直接先从1跑最短路然后直接o(KM)DP就ok了
    当然这样是有问题的
    因为我们必须要先更新dis1小的点
    所以要先按照dis1排个序再去转移就有70pts了
    ③:考虑100pts:
    对于有0边的图,显然直接按照dis1小的是不行的
    e.g a->b->c w=0
    更新顺序显然是a,b,c,然后他又是一个有向图
    所以考虑拓扑排序来确定0边两个端点的更新顺序
    即对于0边,把其加入新图,然后对于新图拓扑排序确定”0点”的更新顺序
    然后对于-1的情况显然是对于一条满足条件的路径上有一个0环
    拓扑排序完了且入度!=0说明这个点在0环上
    同一个0环上任意一个点到1的最短路和到n的最短路都一样
    所以当这个点i,满足dis1[i]+disn[i] (i到n的最短路)<=dis1[n]+K时
    就可以输出-1了
    然后最后排序就是以dis1/disn为第一关键字,拓扑序为第二关键字排序再转移就ok了
    –洛谷题解https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P3953
    Day2
    * 稍微调整了一下心态和策略,所以感觉更好。
    * T1:
    当时感觉还好,听说会爆long long,但是我开了根号,进哥说应该不会有事
    在洛谷上测是满分实际的分数还不知道
    ** 方法
    求出每两个点之间能不能到达,然后转换成bfs判是否联通就行
    * T2:
    一看还以为是最短路,后来又想了一下可能不一定行,但是没想出其他方法了
    于是就用一个dijkstra加点判断(因为是求每段弧最小值所以我就求了每个点到终点(后面没有与之相连的点)的最短路之和取最小值)
    ** 方法
    状压DP:
    1.设f[dep][S]为到第dep层目前用了S状态的点的总方案数目
    考虑在它的补集里面枚举下一层的点数,然后直接转移就行了。
    f[i + 1][s1 | s2] = min(f[i + 1][s1 | s2], f[i][s1] + val[s2][s1]); 其中val[s2][s1]表示s2的每个点到s1所需要的最小总花费。
    如果直接做的话是O(3^n * n^3)的
    发现val这个东西对于每一个s1s2是一样的,那么预处理一下
    先预处理点到集合的,在处理集合到集合的。
    总复杂度O(3^n * n)
    常数有点大。
    2.n<=12,状压.
    起点自定.
    每条边的贡献为层数*边权.
    每个点的连边不需要在意,只关注它对状态产生的贡献.(对于每个状态预处理,复杂度为O(2^n*n)).
    从小到大枚举层数,然后枚举初始状态,再枚举转移后状态.由于预处理,复杂度为O(3^n*n).
    (枚举转移的状态时,顺便统计每个新增点的贡献,而这个已经预处理完了)
    总体来说是O(2^n*n+3^n*n)的
    –由于并不会所以附上luogu答案
    –https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P3959
    * T3:
    写这题的时候时间已经不多了,而且当时还以为最后一题是DP就没怎么敢写(然而后来进哥说就是线段树,我前一周才弄的呀)
    结果写了个暴力就快下考了
    ** 方法
    首先有一个非常明显的暴力是这么写的:
    直接拿一个二维数组维护在每一个位置上的编号,然后暴力对齐就可以了
    通过观察可以发现删除(x,y)位置的点,然后添加到(n,m)
    只会影响到第x行和第m列,所以每次只要修改n+m个元素的位置
    具体参考代码1(略过)
    对于20%的数据,我们只要维护那些被修改过的行和最后一列,可以做到O(nq)
    对于30%x=1的数据,我们发现只要在第一行删掉一个元素,然后放在(n,m),后面的元素整体往前
    其实就是维护一个序列,一开始有多个元素,支持查找第x个元素并删除,以及在最后添加元素
    我们可以用线段树维护,记录区间内有多少个可用点,
    如果我们要查找第x个,而左区间有k个,若x<=k那么就往左区间走,否则在右区间查询第x-k个元素
    具体参考代码2(略过)
    对于100%的数据,我们发现其实就是维护n+1个序列,支持查找和删除第x个元素,以及在最后添加元素
    前n个序列维护每一行的前m-1个元素,最后一个序列维护最后一列的元素
    但是这样的话需要建n+1颗线段树,无法承受
    但是可以发现一开始线段树中的元素是满的,并且一开始的元素编号十分有规律,可以直接计算
    那么我们一开始就直接把所有线段树当作满的,用动态开点每次删点,后面加进来的点用vector储存,
    当删除(x,y)时
    如果y =m那么只要操作最后一列,如果是原来最后一列的元素,就直接计算,否则从vector中提取
    如果y!=m那么先对第x行进行一次操作,转化成删除(x,m)位置的元素,然后再对最后一列进行操作
    因为每次操作最多会加入两个元素,所以最多只会有O(2q)的元素储存在vector中
    时间复杂度O(nlogn),空间复杂度O(nlogn)
    具体参考代码3(略过)
    因为是线段树,所以代码可以应该算比较短的,也非常好写
原创粉丝点击