Codeforces 1043G
不难证明出现的答案答案是不大于 (4) 的,否则是 (-1),构造形如:(BACAD)
直接分开讨论四种答案对应的若干情况
-
(AAAAdots) 的是(lcp)
-
(AAB,BAA) 写一个优秀的拆分,(ABA) 是查 ( m{border})
考虑这样一个结论:子串存在的长度 (ge sqrt n) 的 (border) 一定满足 (l) 和 (st) 的后缀数组上的排名不超过 (sqrt n)
证明考虑如果有超过 (sqrt) 个后缀满足在这两个中间,那么其和两端的 ( m{LCP}) 都大于 (sqrt n),明显不存在
-
( m{ABAC,BACA}) 这个部分直接判是不是两端的字符出现了两次即可
( m{BAAC}) 写出来优秀的拆分之后维护 (l_i) 表示从当前点开始最小的位置满足该子串是一个 (AA) 串,是一个 (RMQ)
四合一挺难受的
平面最近点对
记录本题分治做法
考虑和建立 ( m{KD-Tree}) 的做法类似的,对于当前分治区间 ([l,r]),取 (x) 这维的中位数,递归左右部分,求得两个子集的 (min)
考虑合并两边的信息不难发现只有 ( m{[Mid_x-min,Mid_x+min]}) 中的点有用
将区间里面的点按照 (y) 轴归并之后将这些点取出,写一个带朴素剪枝的暴力即可
考虑这个暴力的正确性:
对于一个点,可能更新答案的点必然在不大于 ([min imes 2min]) 的一个矩形里面
将其分割成 (2 imes 3) 的几个小部分,那么每个矩形框里必然不能有超过 (1) 个点,小矩形对角线的长度为 (frac{5}6 min),那么复杂度正确,而且常数不大
Luogu3322
关注到是一个深度为 (n) 的线段树的形式,那么这个操作的先后顺序和是不是能翻成一个标准序列无关
所记录的东西就是操作的次数,并从小向大 (
m{dfs}),最后 ans+=fac[num];
那么找到所有不是 (+1) 递增的段,如果超过两个一定不合法,剩下的枚举所有情况交换递归
注意在不合法状态有 (2) 个的情况下,如果得到左 (1) 的解可以不枚举右 (2),通过这样的剪枝得到的复杂度为 (2^{24})
联通欧拉图计数
定义是能经过每个边恰好一次并回到出发点的图
设 (f_i) 表示有 (n) 个点的欧拉图的数量(不保证联通),因为图的性质(每个点度数都是偶数),那么结果是 (2^{inom {i-1}2})
设联通欧拉图的数量为 (g_i),枚举 (1) 号点所在的联通块的大小
想要分治 (NTT) 的话好像也不是不行,拆开组合数做即可
Codeforces986E
差分询问,运用质数个数少,次数小等和 (gcd) 本质是次数取 (min) 等性质 (dfs) 处理即可
Codeforces809E
首先得到这样一个东西:
证明直接拿质因子拆就行了,式子变成:
显然枚举 (gcd,) 接着把 (a_i) 扔到其所有因子的 ( m{vector}) 里面,设如下两个函数(没想到吧,竟然是标准的莫比乌斯反演式!)
那么把 (dist) 拆开,对每个质因子整个虚树就能得到 (F(d))
因为要求 (nlog n) 次 (LCA),那么建议使用倍增
本来还想拆 (varphi=mu * Id) 的,同时学会了简单证明了这个式子
Codeforces639F
不难发现题目中要求的就是一个边双,那么先把原图按照边双整成一个森林
对于每个询问,把边和点的所有集合全拉出来建虚树森林,在上面照样跑边双就行了
如何写边双?
inline void tarjan(int x,int pre){
dfn[x]=low[x]=++tim;
for(reg int i=head[x];i;i=e[i].nxt){
int t=e[i].to; if(!dfn[t]){
tarjan(t,i); low[x]=min(low[x],low[t]);
if(low[t]>dfn[x]) bridge[i]=bridge[i^1]=1;
}else if(i!=(pre^1)) low[x]=min(low[x],dfn[t]);
} return ;
}
点双直接判是不是 (dfn[x]le low[t]) 然后弹栈,记割点如下
if(x!=rt||cvcc>1) cut[x]=1
其中 ( m{rt}) 指 ( m{tarjan}) 开始的地方
Codeforces925E
有一些显然的思考是把每个点的点权先整成 (-t_i),有变成白点的操作就给链加
先树剖,然后对树分块,每次修改暴力跳重链按题意模拟即可
Codeforces896C
使用珂朵莉树,其本质是使用了数据随机的情况下会有 (frac{1}4) 的概率是区间覆盖操作
大体就是维护一个里面表示区间的 (set),每个区间的值是相同的
核心函数就是 assign(),split()
,分别表示将区间覆盖和拆开区间,剩下全是直接暴力
实现上有些新的语法:
-
mutable:
能避开 ( m{set}) 里面的 ( m{const}) 限制 -
erase(iterator_L,iterator_R):
删掉 ( m{set}) 里面 ( m{[L,R)}) 的迭代器
BZOJ3786
( m{Euler Tour Tree}) 用欧拉序(( m{in,out}) 各在序列里面加入一次 )来维护子树
外层不论套 ( m{splay}) 抑或是 ( m{fhq\_Treap}) 都可以直接对着序列来交换子树
但是最大的局限性一是维护森林将十分复杂,二是不能换根
本题树上维护 ( m{tag}) 即可,再附 ( m{fhq\_Treap}) 的写法
inline int merge(int x,int y){
if(!x||!y) return x+y;
push_down(x);
push_down(y);
if(rand()%(sz[x]+sz[y])<sz[x]){
rs[x]=merge(rs[x],y);
push_up(x);
return x;
}else{
ls[y]=merge(x,ls[y]);
push_up(y);
return y;
}
}
inline void split(int rt,int siz,int &x,int &y){
if(!rt) return x=y=0,void();
if(sz[rt]==siz) return x=rt,y=0,void();
if(!sz[rt]) return x=0,y=rt,void();
push_down(rt);
if(sz[ls[rt]]>=siz){
split(ls[rt],siz,x,ls[rt]);
y=rt,fa[x]=0;
}else{
split(rs[rt],siz-sz[ls[rt]]-1,rs[rt],y);
x=rt,fa[y]=0;
}return push_up(rt);
}
Codeforces878C
不难发现如果两个选手不是所有维度大小性相同,那么都有可能获胜
所以对于这样的选手缩一把点,维护一个点的个数
考虑到 (set) 的判等方式是两者的 ( m{operator:<}) 都不成立,那么使用 ( m{set}) 维护一个集合里面的维度最值和集合的大小即可
答案也就是 “最大” 的集合的大小
Luogu4426
考虑树上独立集的求法,暴力 (dp) 可以获得 ( m{10 pts}) 的好成绩
直接套到图上面不太可取,其实冲突的地方只有非树边 (=m-n+1le 11) 条
-
我会二进制枚举!
分 (2^{11}) 次方枚举每条边是上侧点选还是下侧点选,剩下的还是一个朴素 (dp)
值得注意的是可以能有 (E_1) 的下侧点是 (E_2) 的上侧点,所以标记的时候要稍微复杂一点
-
( m{Key Observation}):有很多的 (dp) 是没有用的,同时如果不在任意一对非树边涉及的点的路径上的点的 (dp) 永远不变
建立这 (22) 个点的虚树,别的状态的 (dp) 值可以提前预处理出来,剩下的 (dp) 照样二进制枚举做即可
时间复杂度 (Theta(n+11 imes2^{11}))