国庆上课记录

Day1数据结构(上)

一.堆

例1:找N个数的中位数

利用一个大根堆和一个小根堆。

例2:合并果子反过来

哈夫曼二叉树:将值最小的放在离跟最远的地方。

例3:NOIDAY2t1 BZOJ4198

k叉哈夫曼树,每次取k个最小的合并
如果N-1不能整除(k-1),就补重量为0的串

二.单调栈

每次压入一个数时将比它大的数弹出来。

例1:BZOJ 1606从左往右看

每个牛能看到的牛是一段连续的区间,第p头牛,从p+1,到第一个比该牛高度高的牛。

例2:BZOJ 1113

H[p]==h[q],且p,q之间没有比它小的。

例3:最大矩形覆盖

枚举高度,左右扩展。

例4:BZOJ 3039

给出一个n*m的01矩阵,求一个最大的全1矩阵
依次枚举每一行,h[i]表示向上最多有多少连续的1.
然后就是例3的变式啦。

三.单调队列

对首最优,越往后越差。加入值时将没它优的弹出。查询时判断队首是否在可行区,若不在弹出,直到在可行区。

例1:BZOJ1047

给出一个a*b的矩阵 找出一个n*n的区域使(最大值-最小值)最小。
行求f[i][j]表示固定长度的最大值
列求g[i][j]表示固定长度的最大值
当求最小值时,将所有数取反再求一遍最大值,最后取反即可。

例2:BZOJ2096

从左到右枚举右端点,利用单调队列维护当前区间中的最大值和最小值
如果某一时刻当前区间的最大值和最小值之差超过了k,就向右调整左端点直到差小于等于k为止

例3:BZOJ2276

F[p]表示以p作为右端点,左端点最远扩展到哪。
F[p]单调不减
队首到p-1的最大值,即l的最大值。
如果该值大于队首,将队首弹出,直到合法。

四.线段树

例1:BZOJ1798

双标记

例2:区间最大连续子段和

Lmax=max(lson(lmax),lson(sum)+rson(lmax));
Rmax同理。
Pushup根据两个儿子算出父节点。
Pushdown将父亲的标记传给儿子。

例3:BZOJ 1798

例4:LCA+线段树处理

LCA必须用ST在线算法即复杂度为O(nlogn-n)

例5:BZOJ1858

操作4:sum
操作5:类似最大连续子序列
其它的操作要维护1和0,交换1和0时只需要将维护的值交换。

例6:VIJOS1369

//然而并没有很听懂

例7:BZOJ3401

单调栈
线段树单点修改/区间查询

例8:VIJOS1901

例9:BZOJ3339

首先求出以1为左端点区间的mex(用map判断出现O(nlogn))
然后考虑将左端点右移
考虑带来的影响
x—-next[x-1]-1的mex[x-1]=min(mex[x-1],a[x-1]);
即两个相同的数中,没有该数,该数有可能为最小的未出现的自然数。

例10:2015 PKUSC

求所有区间的mex之和
发现mex单调不减,直接二分出需要修改的区间,然后区间赋值.

五.平衡树Splay_treap

考试。。

1 暴力

前缀和%30
枚举矩形的右下角找可以到达的左上角。

2

∑(xi-ave)^2=Sigma(xi^2-2*ave*xi+ave^2)
=∑xi^2-2*sum[i]^2+2*sum[i]
对于∑(xi+d)^2
=∑xi^2+d^2*N+2*∑xi*d

3 treap

如果不进行序列操作,就用treap,否则才用Splay

Day2:

一.倍增

(1)倍增法求LCA

(2)倍增法求后缀数组

c a a b a a a a b
3 1 1 2 1 1 1 1 2 —>先按第一个字母比较
5 1 2 4 1 1 1 2 3—->比较两个字母,没有的即为后跟一个很小的字符
9 4 6 8 1 2 3 5 7——->比较4个字母
如比较abcd 因为上一次已比较过(ab)/(cd),故这次只需要比较

(3)RMQ问题

例:VIJOS1843 NOIP2013

想到最大生成树,即把边权从大到小排序,顺次取出每一条边,然后建图(注意不能成为环图)。
此时城市的交通网就变成了一棵树,可以证明,这棵树中任意两个节点只可能有一条通路,并且这条通路上边权的最小值即为答案。
相当于最大生成树的最小值。LCA+RMQ

例:BZOJ2093

数轴上有n块石头,跳一步能跳到第k近的石头上,问跳m次后在那块石头?
预处理出f[p][q],表示每个石头会跳2^q次后到哪儿。
然后就可以用2的幂次组合出m.
思考如何预处理?
发现对于每个点离它前k近的数是一段连续的区间[l,r]
对于p++,尝试r++,当a[r+1]-a[p]

例:BZOJ2783

枚举终点,起点一定是它的祖先。
然后二分判断,向上爬树。
和求LCA时相同,我们保证当前sum<=s.
走到不能再走时,判断sum是否等于s即可。
时间复杂度O(nlog^2n)

例:VIJOS1780

首先预处理出所有点A,B各走一步到达的地点。
然后用f[p][q]表示走2^q步
f[p][q][0-2]分别表示A的路程,B的路程以及落脚点

二.DFS序

L[i]表示进入的时间,时间戳++
R[i]表示出去的时间,时间戳不变
若在出去的时候时间戳++,则将进入的时候附为正值,出去的时候附为负值。

BZOJ1103

线段树 区间修改,单点查询(出时时间戳不加)
树状数组单点修改,前缀求和(出时时间戳++,当然注意这只适用于满足减法的运算,比如sum就满足,而min,max就不满足)

扩展:

链上的单点修改,求链的和

(sum(x,y)=sum(x,root)+sum(y,root)-sum(fa[lca(x,y)],root))

DFS序求LCA

如果是欧拉序,直接返回深度最小的就是LCA
如果不是,出时时间戳不增加,返回的值的父亲才是LCA
但注意:第二种方法一定要特判LCA(x,y)==x/y。
C++ log是logn级别的,所以要提前预处理。

例:BZOJ3306

修改点权,查询x子树里点权的最小值,换根
来自ZYF:
换根影响的是子树的范围
一开始以1为根然后如果根换成了rt,然后要查询x子树内的最小值。
我们分情况讨论:
1.若x==rt,则直接输出整棵树的最小值
2.若lca(x,rt)不等于x那么直接输出x的子树内的最小值
3.若lca(x,rt)==x那么我们发现整棵树除了x向下走可以到达rt的子树之外全部成了x在rt为根下的子树,那我们把这棵子树中最接近x的节点y求出,在整个区间中踢掉y在1根下子树的范围即可。

三.树链剖分

两次dfs
第一次dfs,求出轻重边
第二次dfs,将重边连成链,放在数据结构里

考试:

然而有一道水题没有写对,所以—->不写题解了。

Day3图论(上)

一.最短路:

SPFA

void spfa(int x)
{
    if(v[x]){flag=1;return;}
    v[x]=1;
    for(int i=head[x],y;i;i=e[i].next)
     if(d[x]+e[i].wif(flag)break;
     }
    v[x]=0;
}
bool check()
{
    flag=0;
    for(int i=1;i<=n;i++)
    {
        spfa(i);
        if(flag)break;
    }
    return flag;
}

Dijkstra

例:BZOJ1726

求1~n的次短路
D[0][i]表示最短路
D[1][i]表示次短路
当tmp

例:BZOJ2763

有k次可以将边权变为0,求1到n的最小花费
D[i][x]表示到达x,用了i次免费
将原来的边(x,y,w)拆成
((i,x),(i,y),w)以及((i,x),(i+1,y),0)
输出d[k][n]

例:BZOJ1003

考虑f[i][j]表示i到j天已经考虑了变化路线的最小花费
g[i][j]表示i到j天不变化路线的最小花费
g[i][j]显然可以每次跑一次SPFA得出
那么f[i][j]=min(g[i][j],min(f[i][l]+k+f[l+1][j]|i<=l

例:VIJOS1909

将边反向,跑BFS,去除坏点
剩下的求1~n的最短路

例:BZOJ1614

二分答案
判断比mid大的边是否等于mid,调整
边权比mid大的看作1,否则看为0
跑最短路

例:VIJOS1053

判断图中是否有负环
貌似最好的方法是判断一条路径上是否一个点出现了两次

Floyd

例:BZOJ1774

来自ZYF

按点权从小到大加进去
那么dp[i][j][k]的含义就是从i到j只经过点权<=w[k]的点的边权和的最小值
然后另开一个数组DP[i][j][k]表示从i到j只经过点权<=w[k]的点的最小花费
DP[i][j][k]=min(DP[i][j][k-1],dp[i][k][k-1]+w[k]+dp[k][j][k-1])
dp[i][j][k]=min(dp[i][j][k-1],dp[i][k][k-1]+dp[k][j][k-1])
或者说我们只是对点按点权大小重编号了一下

例:BZOJ1491

令f[i][j]表示i到j的最短路长度,g[i][j]表示条数
如果f[i][k]+f[k][j]=f[i][j],k的重要都+=g[i][k]*g[k][j]/g[i][j]
最小生成树

例:BZOJ1601

把水库也看成一个点,然后在水库和农田之间连边为建立水库的费用

例:BZOJ3943

建边为a[i]] xor a[j]
跑最大生成树

例:BZOJ1050

求s到t路径上最大边比最小边的最小值
将边排序,枚举最小边,将大边一条一条加进去
当s,t联通时加进去的边就是最大边
最后注意:
最小生成树是最大边最小的生成树
最小生成树还是第k大边最小的生成树

拓扑排序:

例:CH#57 A

加入优先队列,保证字典序最小

例:BZOJ4010

使标号小的点尽量先处理。
即将边全部反向,倒着求一遍字典序最大的拓扑序,反过来就是解。
啊?你要问我为什么?
反正我不会证╭(╯^╰)╮

例:原创大水题什么鬼

将入度为0的点ans[i]设为1
每当更新其他点时,dp[j]+=dp[i].
最后输出所有出度为0的点的dp值之和

强连通分量

Tarjan

#define for4(i,x) for(int i=head[x],y;i;i=e[i].next)
inline void tarjan(int x)
{
    sta[++top]=x;low[x]=dfn[x]=++ti;
    for4(i,x)if(!dfn[y=e[i].go])
    {
        tarjan(y);
        low[x]=min(low[x],low[y]);
    }else if(!scc[y])low[x]=min(low[x],dfn[y]);
    if(low[x]==dfn[x])
    {
        cnt++;
        for(int y=0;y!=x;)scc[y=sta[top--]]=cnt;
    }
} 
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);

例:BZOJ 1051

先缩点,然后判断出度为0的点的个数
如果是1,则答案就是size
否则就不能保证牛能被所有牛喜欢,答案为0.

例:BZOJ 1093

缩点,求最长链
Dp[i]=max(dp[i],dp[j]+size[x])
注意重边!用map判重

01分数规划

例:BZOJ1486

二分答案。
如果没有负环,l=mid;
如果有,r=mid;

例:POJ2949

对于每个字符串,取出前两个字母和后两个字母,建边,边权为length.
求最大环
还是二分,判断有没有正环,有正环的话,就将r=mid;
判正环可以将所有边取负,然后判负环。

考试

然而一道水题改了很久才改对,心情不好╮(╯▽╰)╭

t1

建立从i到i-1的边,边权为0,跑最短路
水成渣了。。。。
好吧,还有线段树的做法

t2

知道C(i,j)后,知道sum[i-1]和sum[j]中一个,就可以知道全部,所以只要sum[0]全部和其他点全部联通,就可以知道所有的信息。
so,连边,跑出最小生成树即可。

t3

缩点,拓扑排序,找入度为0的点求解。

Day4图论(下)

最大流:

例:BZOJ 1066

将每个石柱拆成两个点,之间的边是石柱的高度。
每个石柱的i2向它能跳到的石柱的i1连边,边权为INF
建立超级源点和汇点,和能达到的石柱连接INF
源点连的边为边权为1

例:BZOJ1305

将每个人拆成3个点
如果互相喜欢连边(i1,j1,1)
如果互相不喜欢连边(i2,j2,1)
限制个数(i3,i1,inf) ( i3 , i2 , k)
最后二分答案,设当前检验x
连边(s,i3,x)(j3,t,x)
判断是否满流

例:BZOJ1532

例:BZOJ 1711

建立源点汇点,源点联向所有的食物,边权为1,所有的饮料联向所有的饮料,边权为1. 将牛拆成两个,中间连容量为1的边,所有喜欢的食品向入点连边,喜欢的饮料向出点连边。
最小费用最大流

经典问题:

一个餐厅在相继的 N 天里,每天需用的餐巾数不尽相同
假设第 i 天需要 ri块餐巾
餐厅可以购买新的餐巾,每块餐巾的费用为 p 分
或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分
或者送到慢洗部,洗一块需 n 天(n>m),其费用为 s

题解:

将餐厅分为要用的和用完的
建图
Xi表示第i天用完的餐巾
Yi则是第i天需要的餐巾
S->yi费用为p 买新的
Xi->xi+1费用为0 留到下一天洗
Xi->yi+m 费用为f 快洗
Xi->yi+n 费用为s 慢洗
从S向Xi连接容量为ri的边作为限制
Yi与T连接容量为ri的边作为限制

同BZOJ 1927

同CH#47t1

最小割

记住最小割=最大流

BZOJ1266

Spfa如果d[s][u]+w+d[v][t]==d[s][t],那么说明这条边可能出现在最短路上
将可能在最短路上的边建立新图,跑最短割。
即截断这些边后,只能经过其它的边才能够从s到t.

例:BZOJ1497

一开始将所有的收益加起来
然后求最小的代价
S连用户,边权为c[i]
用户和需要的中转站连边,边权为INF
中转站连向T,边权为p[i]
求最小割

例:BZOJ2127

Day5 数论

快速幂,快速乘

例:BZOJ 2751

显然所有可能数的和的乘积就是答案。
又可发现至少有m-k个位置1~n都可以取,这部分用快速幂计算即可。
剩下的就暴力。。
算数基本定理

例:BZOJ 1986

只要计算x的倍数有多少,显然将n/x向下取整加起来就是答案。

欧几里得算法

Int gcd(int a,int b){return b?gcd(b,a%b):a;}
Lcm(a,b)*gcd(a,b)=a*b

扩展欧几里得:

裴蜀定理有云:gcd(a,b)是a和b能线性组合出来的最小正整数

 void exgcd(int a,int b,int &x,int &y)
{
     if(!b){x=1;y=0;return;}
     exgcd(b,a%b,x,y);
     int t=x;x=y;y=t-(a/b)*y;
}

例BZOJ1407

ci+x*pi≡cj+x*pj (mod M) => (pi-pj)*x ≡cj-ci(mod M)
让这个同余方程无解,或解出的最小的x比两个人中任何一人的寿命长。
M的初始值为max{c[i]},不行,M就++

欧拉定理

令phi(i)表示1-i中与i互质的数的个数
那么如果gcd(a,b)==1,那么a^phi(b)=1 (mod b)
当b是质数的时候,我们得到了费马小定理a^(b-1)=1 (mod b)

逆元 a*x=1 (mod b)

Gcd(a,b)必须为1
用扩展欧几里得求逆元。
当b是质数,利用费马小定理
A^(b-1)=1(mod b) a*a^(b-2)=1(mod b)

如何用O(n)预处理出1~n的逆元?

考虑从1-n求解inv[i]
Inv[1]=1.
(p+i)和i对p的逆元相同。
不妨认为p=i*a-j
那么i*a-j=0 (mod p)
从而 i*a=j (mod p)
所以 i*a*inv[j]=j*inv[j]=1(mod p)
所以i的逆元就是a*inv[j] 因为j

中国剩余定理

Ti=Ai的逆元
X=Σ(ai*ti*Mi)
组合数取模
C(n,m)=n!/(m!(n-m)!)
C[i][j]=c[i-1][j]+c[i-1][j-1]

lucas定理:p是素数

lucas(n,m,p)=lucas(n/p,m/p,p)*c(n%p,m%p)%p
(n1n2n3..nm)的逆元=n1的逆元n2的逆元……*nm的逆元

BZOJ1951

999911658=2*3*4679*35617 所以我们就可以分开来做然后CRT合并。
分开就是求组合数mod 一个小质数
直接预处理+lucas即可
Day 6
别问我为什么没有笔记(巨雾
好啦
我们来说

考试

t1

分两种关系,父子,兄弟
然后记录子树之和(包括自己)
然而取模什么的又挂了4个点
——>我对不起党的栽培,对不起劳苦大众

t2

并查集,特判两人互相暗恋
然后统计强连通分量的个数,求阶乘
再乘上强连通分量里个数大于1那么多的2的幂次

t3

好吧,状态压缩
f[i][j[k]表示用了i个数,状态为j,最后一位为k的方案数。
转移的话,可以直接判断是否可行
若不可行 ans+=f[i][j][k]*(n-i-1)!

别问我为什么思路都会还考那么水。。

Day7

最后一天,bless all.

你可能感兴趣的:(生活,知识点讲解,进击的OIer)