利用一个大根堆和一个小根堆。
哈夫曼二叉树:将值最小的放在离跟最远的地方。
k叉哈夫曼树,每次取k个最小的合并
如果N-1不能整除(k-1),就补重量为0的串
每次压入一个数时将比它大的数弹出来。
每个牛能看到的牛是一段连续的区间,第p头牛,从p+1,到第一个比该牛高度高的牛。
H[p]==h[q],且p,q之间没有比它小的。
枚举高度,左右扩展。
给出一个n*m的01矩阵,求一个最大的全1矩阵
依次枚举每一行,h[i]表示向上最多有多少连续的1.
然后就是例3的变式啦。
对首最优,越往后越差。加入值时将没它优的弹出。查询时判断队首是否在可行区,若不在弹出,直到在可行区。
给出一个a*b的矩阵 找出一个n*n的区域使(最大值-最小值)最小。
行求f[i][j]表示固定长度的最大值
列求g[i][j]表示固定长度的最大值
当求最小值时,将所有数取反再求一遍最大值,最后取反即可。
从左到右枚举右端点,利用单调队列维护当前区间中的最大值和最小值
如果某一时刻当前区间的最大值和最小值之差超过了k,就向右调整左端点直到差小于等于k为止
F[p]表示以p作为右端点,左端点最远扩展到哪。
F[p]单调不减
队首到p-1的最大值,即l的最大值。
如果该值大于队首,将队首弹出,直到合法。
双标记
Lmax=max(lson(lmax),lson(sum)+rson(lmax));
Rmax同理。
Pushup根据两个儿子算出父节点。
Pushdown将父亲的标记传给儿子。
LCA必须用ST在线算法即复杂度为O(nlogn-n)
操作4:sum
操作5:类似最大连续子序列
其它的操作要维护1和0,交换1和0时只需要将维护的值交换。
//然而并没有很听懂
单调栈
线段树单点修改/区间查询
首先求出以1为左端点区间的mex(用map判断出现O(nlogn))
然后考虑将左端点右移
考虑带来的影响
x—-next[x-1]-1的mex[x-1]=min(mex[x-1],a[x-1]);
即两个相同的数中,没有该数,该数有可能为最小的未出现的自然数。
求所有区间的mex之和
发现mex单调不减,直接二分出需要修改的区间,然后区间赋值.
前缀和%30
枚举矩形的右下角找可以到达的左上角。
∑(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
如果不进行序列操作,就用treap,否则才用Splay
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),故这次只需要比较
想到最大生成树,即把边权从大到小排序,顺次取出每一条边,然后建图(注意不能成为环图)。
此时城市的交通网就变成了一棵树,可以证明,这棵树中任意两个节点只可能有一条通路,并且这条通路上边权的最小值即为答案。
相当于最大生成树的最小值。LCA+RMQ
数轴上有n块石头,跳一步能跳到第k近的石头上,问跳m次后在那块石头?
预处理出f[p][q],表示每个石头会跳2^q次后到哪儿。
然后就可以用2的幂次组合出m.
思考如何预处理?
发现对于每个点离它前k近的数是一段连续的区间[l,r]
对于p++,尝试r++,当a[r+1]-a[p]
枚举终点,起点一定是它的祖先。
然后二分判断,向上爬树。
和求LCA时相同,我们保证当前sum<=s.
走到不能再走时,判断sum是否等于s即可。
时间复杂度O(nlog^2n)
首先预处理出所有点A,B各走一步到达的地点。
然后用f[p][q]表示走2^q步
f[p][q][0-2]分别表示A的路程,B的路程以及落脚点
L[i]表示进入的时间,时间戳++
R[i]表示出去的时间,时间戳不变
若在出去的时候时间戳++,则将进入的时候附为正值,出去的时候附为负值。
线段树 区间修改,单点查询(出时时间戳不加)
树状数组单点修改,前缀求和(出时时间戳++,当然注意这只适用于满足减法的运算,比如sum就满足,而min,max就不满足)
(sum(x,y)=sum(x,root)+sum(y,root)-sum(fa[lca(x,y)],root))
如果是欧拉序,直接返回深度最小的就是LCA
如果不是,出时时间戳不增加,返回的值的父亲才是LCA
但注意:第二种方法一定要特判LCA(x,y)==x/y。
C++ log是logn级别的,所以要提前预处理。
修改点权,查询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,将重边连成链,放在数据结构里
然而有一道水题没有写对,所以—->不写题解了。
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;
}
求1~n的次短路
D[0][i]表示最短路
D[1][i]表示次短路
当tmp
有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]
考虑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
将边反向,跑BFS,去除坏点
剩下的求1~n的最短路
二分答案
判断比mid大的边是否等于mid,调整
边权比mid大的看作1,否则看为0
跑最短路
判断图中是否有负环
貌似最好的方法是判断一条路径上是否一个点出现了两次
来自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])
或者说我们只是对点按点权大小重编号了一下
”
令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]
最小生成树
把水库也看成一个点,然后在水库和农田之间连边为建立水库的费用
建边为a[i]] xor a[j]
跑最大生成树
求s到t路径上最大边比最小边的最小值
将边排序,枚举最小边,将大边一条一条加进去
当s,t联通时加进去的边就是最大边
最后注意:
最小生成树是最大边最小的生成树
最小生成树还是第k大边最小的生成树
加入优先队列,保证字典序最小
使标号小的点尽量先处理。
即将边全部反向,倒着求一遍字典序最大的拓扑序,反过来就是解。
啊?你要问我为什么?
反正我不会证╭(╯^╰)╮
将入度为0的点ans[i]设为1
每当更新其他点时,dp[j]+=dp[i].
最后输出所有出度为0的点的dp值之和
#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);
先缩点,然后判断出度为0的点的个数
如果是1,则答案就是size
否则就不能保证牛能被所有牛喜欢,答案为0.
缩点,求最长链
Dp[i]=max(dp[i],dp[j]+size[x])
注意重边!用map判重
二分答案。
如果没有负环,l=mid;
如果有,r=mid;
对于每个字符串,取出前两个字母和后两个字母,建边,边权为length.
求最大环
还是二分,判断有没有正环,有正环的话,就将r=mid;
判正环可以将所有边取负,然后判负环。
然而一道水题改了很久才改对,心情不好╮(╯▽╰)╭
建立从i到i-1的边,边权为0,跑最短路
水成渣了。。。。
好吧,还有线段树的做法
知道C(i,j)后,知道sum[i-1]和sum[j]中一个,就可以知道全部,所以只要sum[0]全部和其他点全部联通,就可以知道所有的信息。
so,连边,跑出最小生成树即可。
缩点,拓扑排序,找入度为0的点求解。
将每个石柱拆成两个点,之间的边是石柱的高度。
每个石柱的i2向它能跳到的石柱的i1连边,边权为INF
建立超级源点和汇点,和能达到的石柱连接INF
源点连的边为边权为1
将每个人拆成3个点
如果互相喜欢连边(i1,j1,1)
如果互相不喜欢连边(i2,j2,1)
限制个数(i3,i1,inf) ( i3 , i2 , k)
最后二分答案,设当前检验x
连边(s,i3,x)(j3,t,x)
判断是否满流
建立源点汇点,源点联向所有的食物,边权为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的边作为限制
Spfa如果d[s][u]+w+d[v][t]==d[s][t],那么说明这条边可能出现在最短路上
将可能在最短路上的边建立新图,跑最短割。
即截断这些边后,只能经过其它的边才能够从s到t.
一开始将所有的收益加起来
然后求最小的代价
S连用户,边权为c[i]
用户和需要的中转站连边,边权为INF
中转站连向T,边权为p[i]
求最小割
显然所有可能数的和的乘积就是答案。
又可发现至少有m-k个位置1~n都可以取,这部分用快速幂计算即可。
剩下的就暴力。。
算数基本定理
只要计算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;
}
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)
Gcd(a,b)必须为1
用扩展欧几里得求逆元。
当b是质数,利用费马小定理
A^(b-1)=1(mod b) a*a^(b-2)=1(mod b)
考虑从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(n,m,p)=lucas(n/p,m/p,p)*c(n%p,m%p)%p
(n1n2n3..nm)的逆元=n1的逆元n2的逆元……*nm的逆元
999911658=2*3*4679*35617 所以我们就可以分开来做然后CRT合并。
分开就是求组合数mod 一个小质数
直接预处理+lucas即可
Day 6
别问我为什么没有笔记(巨雾
好啦
我们来说
分两种关系,父子,兄弟
然后记录子树之和(包括自己)
然而取模什么的又挂了4个点
——>我对不起党的栽培,对不起劳苦大众
并查集,特判两人互相暗恋
然后统计强连通分量的个数,求阶乘
再乘上强连通分量里个数大于1那么多的2的幂次
好吧,状态压缩
f[i][j[k]表示用了i个数,状态为j,最后一位为k的方案数。
转移的话,可以直接判断是否可行
若不可行 ans+=f[i][j][k]*(n-i-1)!
最后一天,bless all.