码谷国庆游记Day1-Day3

北大大佬wdyhy为我们上了前三天的课
下面是总结

ay3)

Day1

讲课内容——基础算法
一、前缀和

1.一维前缀和:

  • sum[i]=sum[i-1]+a[i];

2.二维前缀和:

  • sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]
  • 令b[i][j] = Σa[i][k] (k<=j)
    sum[i][j]=sum[i-1][j]+b[i][j];
    ↑对于第一维把第二维的前缀和求出来,然后再把第一维的前缀和加起来就是二维前缀和了
二、差分

1.一维差分:

  • 一维数组b[i]=a[i]-a[i-1],我们发现对b[i]求前缀和之后就是数组a.
    sum[i]=b[1]+b[2]+…b[i]
    =a[1]-a[0]+a[2]-a[1]+…a[i]-a[i-1]
    =a[i]
  • 二维差分
    多组询问,如何对于一个二维数组求出一个矩阵(x1,y1,x2,y2)中所有元素的和.
    sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1].
T1&T2【差分】

【题目描述】
给你一个长为n的序列,有两种操作,一种是读入(l,r,x)表示对于区间[l,r]中每个数的值加上x,另一种是查询[l,r]内的数的和是多少.
n<=100000,第一种操作个数<=100000,第二种操作个数<=100

【思路】
对序列差分,我们发现一次操作对差分序列的影响只有b[l],b[r+1],那么就在b[l]+x,在b[r+1]-x即可.(b[l]=(a[l]+x)-a[l-1]=原b[l]+x; b[r+1]同理可证)

又∵第二种操作个数很少,∴第二种操作每次暴力求出前缀和来即可.

STL库一些函数的使用:

三、堆
  • 堆在stl库中是优先队列,他维护的是一个单调递增或单调递减的数列.可以满足每次插入一个数,查询堆顶(最大/最小数),删除堆顶.

  • priority_queue q;表示定义一个大根堆.堆中的元素从大到小排序.
    priority_queue<int,vector,greater >q;表示小根堆
    q.push(x);把x加入到堆中.
    q.pop();弹出堆顶
    q.empty()表示判断q是否空了.
    q.size();表示q的大小.

T3合并果子【堆】

【题目描述】
你有n堆果子,每次你可以合并任意两堆果子,消耗的体力值是这两堆果子个数和,问你最小消耗多少体力.

【思路】
每次可以贪心合并最小的两堆果子,这样一定是最优的,因为你一定会合并n-1次,这样保证每次合并的两堆果子一定最小

用小根堆维护即可,即每次弹出最小的两个果子,然后合并起来再放到堆里.

T3中位数LuoguP1168【堆】

【题目描述】
给出一个长度为N的非负整数序列Ai,对于所有1≤k≤(N+1)/2,输出A1, A3, …, A2k-1的中位数。即前1,3,5,…个数的中位数。
n<=100000
【思路】
先考虑求1~n的中位数,把数组排序最中间的数就是答案.考虑怎么用堆来维护这个答案.

对于一个长为i的序列,其中i为奇数.
记x表示这个序列的中位数,然后我们用大根堆维护前一半小的数,用小根堆维护后一半小的数,这样序列分为了[1,(i-1)/2]∪[(i+1)/2,i]

现在考虑加进去一个数w
如果w<=x那么w要放到[1,(i-1)/2]中,同时因为加入了w,相应的就要把x放到[(i+1)/2,i]中,然后把[1,(i-1)/2]中的堆顶作为x.
如果w>x相反.
也就是我们要时刻维护[1,(i-1)/2]和[(i+1)/2,i]这两个堆大小相同.

T5序列合并LuoguP1631【堆】

【题目描述】
给你两个大小为n的序列A和B,把这两个数列中的数两两相加,得到n2个数,问这n2个数中前n小的是多少.
n<=100000

【思路】
把A和B排序然后维护两个指针p1,p2.
一开始p1=p2=1,然后维护一个struct(p1,p2,a[p1]+b[p2]).
其中对于第三关键字a[p1]+b[p2]维护小根堆.
每次弹出堆顶(p1,p2,a[p1]+b[p2]),然后再把(p1+1,p2,a[p1+1]+b[p2])和(p1,p2+1,a[p1]+b[p2+1])加入到堆中.
当我们弹出了n次就结束即可.
因为只会弹出n次,每次弹出只会新增加两个数,所以堆中只会有O(n)级别的数.
复杂度nlogn.

四、map
  • map就是一个映射.
    对于一个数x,我想知道它的映射f(x)是多少,但是x可能是一个10^9级别的数f(x)开不下.
  • map中使用的内存是你向map中添加的元素个数.
    map<long long,int> mp.
    表示mp[(ll)x]=(int)数组下标可以是long long级别的数,表示的数是int类型的.
  • map常常在字符串哈希中用到.

洛谷大佬关于map文章

五、set
  • set实际上是颗平衡树.,维护的是维护元素递减的数据结构
    支持加入一个数,删除任意一个数,查询最小值,查询最大值,查询数x是否存在…
    set维护的是一个集合,加入有两个x,在set中其实只有一个x.
  • set st;
    st.insert(x); 插入x
    st.erase(x); 删除x
    st.begin(); 返回的是最小值的指针.前面加就是最小值即(st.begin());
    st.rbegin(); 返回的是最大值的指针

洛谷大佬关于set文章

六、vector
  • vector相当于邻接表,但是不开o2会比邻接表慢一点,但是vector很好用.
  • vector a[n];
    表示对于每个a[i]是一个邻接表,邻接表中的元素是int类型的.
  • vec.push_back(x)表示将元素x加入到vec中,vec下标是从0开始的.
    vec.size()表示的是vec的大小.
    vec.clear();表示清空vec
    vector比邻接表好的在于它可以排序.
    sort(vector.begin(),vector.end(),cmp);
七、单调栈
  • 栈是一个先进后出的数据结构.
    单调栈就是维护单调性的东西.
T6模板【单调栈】

【题目描述】
给你一个数组a[n],求对于每一个i,在1~i-1中离i最近的比i大的数是多少.没有输出0.
n<=100000

【思路】
单调栈维护一个递减的序列.
a[st[1]]>=a[st[2]]>=a[st[3]]>=…a[st[t]].
当我们枚举i时如果a[i]>=a[st[t]],就把t–,直到一个a[st[t]]>a[i],这个时候st[t]就是大于a[i]离i最近的数.

八、单调队列
  • 队列是一个先进先出的数据结构.
    单调队列就是维护单调性的队列.
T6滑动窗口LuoguP1886【单调队列】

【题目描述】
给你一个数组a[n],对于每个i,求[i-k+1,i]中的最大值和最小值.

【思路】
最大值:单调队列在加入元素的时候是和单调栈一样的.
考虑维护最大值,那么就用单调队列维护一个递减的序列,a[q[h]]>=a[q[h+1]]>=…>=a[q[t]]那么最大值就是单调队列的队头的数.
因为有一个[i-k+1,i]的限制,所以单调队列要维护队头,而单调栈不用

怎么处理[i-k+1,i]呢?
while (q[h]<i-k+1) h++;
因为单调队列维护的序列单调递减,而维护的下表是单调递增的,所以每次暴力弹出即可.
同样最小值的话就用单调队列维护一个单调递增的序列即可.

T8理想正方形LuoguP2216

【题目描述】
有一个hw的整数组成的矩阵,现请你从中找出一个nn的正方形区域,使得该区域所有数中的最大值和最小值的差最小.
h,w<=1000

【思路】
怎么去动态维护一个矩阵的最大值和最小值呢?
先考虑最大值,同样最小值是一样的.
考虑把nn的矩阵看成n个1n的矩阵,然后我们把这n个1*n的小矩阵的最大值求出来得到了a1…an.
也就是我们把二维矩阵拆成了一维.

问题转化为了我们要维护[i-n+1,i]中的最大值和最小值.
这样我们枚举起点i,从(1,i)~(i+n-1,n)这个矩阵开始从上往下每次增加一个大小为1n的矩阵,同时弹出后面一个1n的矩阵即可.
复杂度n^2.

T9bzoj3831【dp+单调队列】

【题目描述】
有一排n棵树,第i棵树的高度是d[i],MHY要从第一棵树到第n棵树去找他的妹子玩.
如果MHY在第i棵树,那么他可以跳到第i+1,i+2,…,i+k棵树.
如果MHY跳到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会.
为了有体力和妹子玩,MHY要最小化劳累值。
n<=10^6

【思路】
设dp[i]表示到i的最小花费.可以把dp转移看作这样
dp[i]=min(dp[j]+1) (i-k≤j<i)
dp[i]=min(dp[j]) (i-k≤j<i&&h[j]>h[i])

我们发现一个特殊性质,每次花费的体力只有1.
因此维护一个单调队列,使得队列中的dp[i]从小到大递增,dp值相同的树高度选更大的.
因为跳到更高的树代价只有1,因此我们只要看单调队列中dp最小值能否直接跳过来即可.如果直接跳不过来那么选次小值一定不优.

九、并查集
  • 并查集就是维护联通性的东西.
  • 路径压缩并查集是O(n)的
    路径不压缩并查集是O(n^2)的
  • 路径不压缩并查集可以看做将连通性转化为一棵树,如果每个点一次次往上跳到根节点是O(n)的
    路径压缩并查集可以看做一个菊花图,每个点跳到根是O(1)的.
T10LuoguP2078【并查集】

【题目描述】
在某个城市里,住着 n 头猪,他们经常抢东西吃,任何两头猪要么是朋友,要么是敌人,并 且满足 1,我的敌人的敌人就是朋友.2,我的朋友的朋友还是朋友.他们分为了好多猪圈,在 同一个猪圈里的都是朋友.问这个城市里有几个猪圈.
n<=100000

【思路】
并查集模板题. 考虑对每个猪 i,建立虚点 i+n. 如果两个人 x,y 是朋友那么就用并查集把 x 和 y 连接起来. 如果两个人 x,y 不是朋友,那么就用并查集把 x 和 y+n,y 和 x+n 连接起来. 最后求出有多少个联通块即可.

T11LuoguP1196【并查集】

【题目描述】
初始有n个独立的舰船,有T组操作,有两种操作Mi,j表示将i舰船所在的舰队接到j舰队尾部,Ci,j表示询问i,j舰船是否在同一舰队,如果在,输出i,j之间隔了几个舰船.
n<=10^5
T<=5*10^5

【思路】
设f[i]表示i所在联通块编号,cnt[i]表示i所在联通块大小,up[i]表示i到i所在舰队的头部一共有几个舰船,那么对于查询x,y之间的舰船就是up[x]-up[y]-1个.
在路径压缩时维护up数组,up[k]+=up[f[k]]即可.

T12AtcoderE【并查集】

【题目描述】
一个n个点的树,每条边有边权,要求对于每个点i,从i点到其他的n-1个点的距离为两个点间边权最小值,求每个点i到其他n-1个点的距离之和.
n<=100000

【思路】
按照边权从大到小排序然后加入到树中,每加入一条边一定连接了两个联通块x和y,记每个联通块大小为sz[i],那么对于x联通块中每个点的答案增加了sz[y]*边权,y中每个点增加了sz[x]*边权.
用并查集维护联通块,同时启发式合并在并查集上打标记即可.

并查集标记考虑在启发式合并时下传给每个点,合并后再打一个标记表示一个联通块中每个点要增加多少权值.

十、倍增
  • 倍增其实就是相当于二进制拆分,所以倍增的时间复杂度是log的.

LCA

  • lca,就是指树上两点x,y的最近公共祖先.
    对于树上两个点x,y,假设x的深度比y大,那么就先让x跳到和y深度相同的祖先上,然后x和y深度相同同时向上跳,直到跳到一个点x和y相遇了为止.

  • 设f[i][j]表示i点向上跳2^j步跳到哪个点.
    如何处理f数组?

  • 注意:2的j次方+2的j次方=2^(j+1).
    那么f[i][j]数组就可以递推了.
    对于每个i,假设现在已经处理了f[i][0~j-1]的值,那么只要能更新f[i][j]那么就可以递推了.
    一开始f[i][0]表示i的父亲.
    f[i][j]=f[f[i][j-1]][j-1]
    表示从i跳2^(j-1) 到f[i][j-1],然后又跳了2^(j-1)到了f[f[i][j-1]][j-1].

  • 考虑我们现在要从x开始向上跳d步,因为我们f[i][j]表示的是跳2^j步上去的,所以要通过跳很多次凑出d步来.
    有一个性质是每个数都可以被若干个不同的2的幂加起来表示.
    设d=2a1+2a2…
    那么我们考虑贪心,从高到低枚举二进制位,假设当前在点x,如果2的j次方<=d,那么就令x向上跳2的j次方,并且d-=2^j.

  • 那么求lca就可以在log的复杂度里做了,假设x的深度比y大,那么x就要向上先跳dep[x]-dep[y]步,现在dep[x]=dep[y].
    如果x=y那么lca就是x
    如果x!=y

  • 我们令x和y同时向上跳,在跳的时候从大到小枚举二进制位j表示跳2的j次方,如果x和y同时跳2的j次方步后跳到一起了,那么就不跳(因为可能跳到lca跳过了),否则就让x和y同时跳2^j步,这样他们跳到最后的时候就差一步就可以跳到lca了.
    最后再让x和y跳一步就是lca了.

  • 倍增相关数组和倍增数组的转移方式都是类似的.
    比如w[i][j]表示从i向上跳2^j步经过边的最小值是多少.
    w[i][j]=min(w[i][j-1],w[f[i][j-1]][j-1]).

T13 poj1679【倍增】

【题目描述】
n个点,m条边的图,每条边有边权,求这棵最小生成树是否是唯一的,如果存在另一棵树和这棵树权值相同那么说是不唯一的.
n<=100000

【思路】
先建出最小生成树来,然后枚举不在最小生成树上的边(x,y,w),然后找到最小生成树上x到y点之间边权最大值,如果最大值=w,那么说明将w替换最大值后形成树权值相同,最小生成树就不是唯一的了.

显然尽可能走限重大的边更优.
那么就考虑建最大生成树,问题转换为了求两点间限重最小值.
同样维护从i向上2^j步边权最小值即可.

T15JosephLand

【题目描述】
有n个点组成的树,每条边(x,y)表示x到y有一条长度为1的边,然后有m个售票站形如(x,w,d),每个售票站在点x,你可以花w的钱买一张票向上最多走d步.
问每个点到根最少花多少元.

【思路】
n^2dp?
设dp[i]表示从i点到根的最小花费,dp[1]=0
我们从1开始向下转移,保证在每个点x的dp值更新之前他的祖先的dp都被更新过了.
转移?

假如i号点有一个售票站,票价为w,向上跳d步,那么转移就是
dp[i]=min(dp[j]+1)其中j是i的祖先并且dep[i]-dep[j]<=d.
暴力就是从i枚举向上d步的每一个祖先然后更新dp[i].

如何考虑用倍增来优化?
记录w[i][j]表示i点向上跳2^j步的dp值的最小值.
那么对于每个d可以拆成若干个2的幂加起来,也就是可以在log的时间复杂度内通过w[i][j]来优化这个dp了.
时间复杂度nlogn.

十一、倍增RMQ
  • 支持nlogn预处理,o(1)回答区间最小值/最大值的数据结构.

  • 原理就是倍增.
    设f[i][j]表示区间[i,i+2^j-1]这个区间的最小值,转移也很显然
    更新f[i][j]的时候假设已经更新过f[i][0~j-1]了.
    f[i][j]=min(f[i][j-1],f[i+2^(j-1)][j-1]).
    这样就可以在nlogn复杂度预处理了.

  • 如何O(logn)查询?
    考虑和倍增lca一样,可以把这个区间拼成若干个大小为2的幂的区间.
    这样对于这log个区间来更新答案即可.

  • 如何O(1)查询?
    假设区间[l,r],长度为d=r-l+1,我们找到最大的j满足2^j<=d.
    我们用两个区间拼起来可以得到[l,r]
    [l,l+2j-1]和[r-2j+1,r]这两个区间一定覆盖了[l,r]这个区间.
    那么答案就是min(f[l][j],f[r-2^j+1][j]);

  • 注意rmq只能维护区间最大值和最小值,因为它是通过两个区间覆盖得到答案的.

T16bzoj4569

【题目描述】
一个长度为n的大数,用S1S2S3…Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2…Sr1与Sl2Sl2+1Sl2+2…Sr2完全相同。比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,131141不满足条件,前者数的长度不为6,后者第二位与第五位不同。问满足以上所有条件的数有多少个。
注意:不能有前导0

【思路】
n^2暴力?
nlogn?
我们把条件所限制的相等的位置看做一个连通块,那么最后只要知道有几个连通块就可以求出答案来了。
所以一个暴力的做法就是对于每个限制,贪心地把两个区间对应的数通过并查集合并,最后查询多少个连通块即可.
时间复杂度n^2.

solution:
设f[i][j]表示从i开始2j区间和谁对应相同,f[i][j]=f[k][j]=p那么从i开始2j个字符和从k开始2^j个字符是相同的.并且这两个区间都属于并查集p.
考虑对于每个限制
将区间[l,r]分为[l,l+2d-1]和[r-2d+1,r]然后将这两个区间通过并查集合并.
最后处理完所有操作,对于所有的f[i][j]将f[i][j-1],f[i+2^(j-1)][j-1]所对应的并查集分别合并即可.

十二、二分
  • 当我们遇到某些最大值最小或最小值最大之类的问题就可以考虑二分答案.
T17营救LuoguP1396【二分答案】

【题目描述】
该市有m条大道连接n个区,一条大道将两个区相连接,每个大道有一个拥挤度。小明的妈妈虽然很着急,但是不愿意拥挤的人潮冲乱了她优雅的步伐。所以请你帮她规划一条从s至t的路线,使得经过道路的拥挤度最大值最小。

【思路】
使最大值最小显然要先二分答案,是一个左开右闭区间的二分形式.
然后用边权小于等于二分值mid的边看是否能使s到t联通即可.

T18往奥格瑞玛的道路LuoguP1462【二分答案】

【题目描述】
有n个城市,有m条边,每条双向边连接xi,yi,当hly走过这个点时他会掉wi滴血,初始hly有b滴血,hly想从1走到n,每个城市有一个ci表示经过这个城市收费ci,现在hly希望能从1到n在自己不挂掉的情况下经过的城市中收费的最大值最小.
n<=10^5.

【思路】
显然符合二分性,那么二分答案mid,将收费小于等于mid的点加入到图中,然后跑最短路,如果最短路长度小于等于b那么mid就合法.

十三、贪心
T19国王游戏LuoguP1080【贪心】

【题目描述】
有n个人,国王在最前面,n个大臣两只手上分别有一个数,每个大臣奖赏为排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果.
求一种站队的顺序满足奖赏最多的大臣奖赏尽可能少.

【思路】
假设有两个人x,y,在他们之前所有人的左手上数乘积为s.
x在y前的收益为max(s/xb,sxa/yb)
x在y后的收益为max(s/yb,s
ya/xb)
因为sxa/yb一定大于s/yb,sya/xb一定大于s/xb.
所以只要sxa/yb<sya/xb那么x就在y前面.
也就是按照xaxb<yayb排序即可.

设dp[i]表示1~i的最长上升子序列
转移很显然dp[i]=max(dp[j]+1)(a[j]<a[i])
时间复杂度n^2

T20AtcoderD - Forest【并查集+贪心】

【题目描述】
有n个点,m条边形成了n-m个树的森林,每个点有权值val[i],给两个点x,y连边的代价为val[x]+val[y],且用完一次x,y就不能再用x,y了.问最少多少代价能够将这n个点连成一棵树.
n,m<=100000

【思路】
先用并查集求出有cnt个联通块,也就是我们要连cnt-1条边,每次连两个点,所以要选出2*(cnt-1)个点,并且每个联通块都要连至少一次边.
那么我们就先选出每个联通块中最小的点,再从这cnt个联通块中选择2*(cnt-1)-cnt个点即可.
怎么维护?

我们发现每次选完一个点之后要把这个点删掉,并且每次找到所有联通块中最小的点并且删掉.
对于每个联通块维护一个小根堆加入联通块中所有点,然后维护一个全局的小根堆加入所有联通块中最小的点,每次选全局中的最小值然后把相应的联通块中的点也删掉即可.

T21Cover the Tree

【题目描述】
给你一颗大小为n的树,求让你用最少的简单路径使得能够覆盖整棵树所有边.输出方案
n<=10^5.

【思路】
树的重心:树上一个点满足其子树大小最大值最小.
树的重心性质:它的每个子树大小都<=n/2,因为如果有一个子树大小>n/2,那么可以让重心向这棵子树偏移,这样不断调整重心.

找到重心我们发现这棵树被分为好多个<=n/2的子树,那么就可以进行贪心了.
如何通过重心的性质来进行贪心?

考虑把每个子树中的叶子结点放到不同的vector中,开一个堆,每次选择vector最大的两个子树中的两个叶子结点并覆盖相应的路径,然后把这两个叶子结点从相应的子树中去掉即可.

————————————Day1 End————————————

Day2

模拟赛
讲课内容——图论

Day3

模拟赛

爆零的一天

讲课内容——动态规划&杂题选讲
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值