2021-05 杂题泛做

Codeforces 1043G

不难证明出现的答案答案是不大于 4 的,否则是 1,构造形如:BACAD

直接分开讨论四种答案对应的若干情况

  • AAAA 的是lcp

  • AAB,BAA 写一个优秀的拆分,ABA 是查 border

    考虑这样一个结论:子串存在的长度 nborder 一定满足 lst 的后缀数组上的排名不超过 n

    证明考虑如果有超过 sqrt 个后缀满足在这两个中间,那么其和两端的 LCP 都大于 n,明显不存在

  • ABAC,BACA 这个部分直接判是不是两端的字符出现了两次即可

    BAAC 写出来优秀的拆分之后维护 li 表示从当前点开始最小的位置满足该子串是一个 AA 串,是一个 RMQ

四合一挺难受的

平面最近点对

记录本题分治做法

考虑和建立 KDTree 的做法类似的,对于当前分治区间 [l,r],取 x 这维的中位数,递归左右部分,求得两个子集的 min

考虑合并两边的信息不难发现只有 [Midxmin,Midx+min] 中的点有用

将区间里面的点按照 y 轴归并之后将这些点取出,写一个带朴素剪枝的暴力即可

考虑这个暴力的正确性:

对于一个点,可能更新答案的点必然在不大于 [min×2min] 的一个矩形里面

将其分割成 2×3 的几个小部分,那么每个矩形框里必然不能有超过 1 个点,小矩形对角线的长度为 56min,那么复杂度正确,而且常数不大

Luogu3322

关注到是一个深度为 n 的线段树的形式,那么这个操作的先后顺序和是不是能翻成一个标准序列无关

所记录的东西就是操作的次数,并从小向大 dfs,最后 ans+=fac[num];

那么找到所有不是 +1 递增的段,如果超过两个一定不合法,剩下的枚举所有情况交换递归

注意在不合法状态有 2 个的情况下,如果得到左 1 的解可以不枚举右 2,通过这样的剪枝得到的复杂度为 224

联通欧拉图计数

定义是能经过每个边恰好一次并回到出发点的图

fi 表示有 n 个点的欧拉图的数量(不保证联通),因为图的性质(每个点度数都是偶数),那么结果是 2(i12)

设联通欧拉图的数量为 gi,枚举 1 号点所在的联通块的大小

gi=fij=1i1(i1j)fjgij

想要分治 NTT 的话好像也不是不行,拆开组合数做即可

Codeforces986E

差分询问,运用质数个数少,次数小等和 gcd 本质是次数取 min 等性质 dfs 处理即可

Codeforces809E

首先得到这样一个东西:

φ(ai×aj)=φ(ai)×φ(aj)×gcd(ai,aj)φ(gcd(ai,aj))

证明直接拿质因子拆就行了,式子变成:

12(n1)ni=1nj=1nφ(ai)×φ(aj)×gcd(ai,aj)φ(gcd(ai,aj))×dist(i,j)

显然枚举 gcd, 接着把 ai 扔到其所有因子的 vector 里面,设如下两个函数(没想到吧,竟然是标准的莫比乌斯反演式!)

12(n1)nd=1ndφ(d)i=1nj=1n[(ai,aj)=d]φ(ai)φ(aj)dist(i,j)

f(x)=i=1nj=1n[(ai,aj)=x]φ(ai)φ(aj)dist(i,j)=d|xF(d)

F(x)=x|dμ(dx)f(d)=x|di=1nj=1n[d|(ai,aj)]φ(ai)φ(aj)dist(i,j)

那么把 dist 拆开,对每个质因子整个虚树就能得到 F(d)

因为要求 nlognLCA,那么建议使用倍增

本来还想拆 φ=μ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]low[t] 然后弹栈,记割点如下

if(x!=rt||cvcc>1) cut[x]=1

其中 rttarjan 开始的地方

Codeforces925E

有一些显然的思考是把每个点的点权先整成 ti,有变成白点的操作就给链加

先树剖,然后对树分块,每次修改暴力跳重链按题意模拟即可

Codeforces896C

使用珂朵莉树,其本质是使用了数据随机的情况下会有 14 的概率是区间覆盖操作

大体就是维护一个里面表示区间的 set,每个区间的值是相同的

核心函数就是 assign(),split(),分别表示将区间覆盖和拆开区间,剩下全是直接暴力

实现上有些新的语法:

  • mutable: 能避开 set 里面的 const 限制

  • erase(iterator_L,iterator_R): 删掉 set 里面 [L,R) 的迭代器

BZOJ3786

Euler Tour Tree 用欧拉序(in,out 各在序列里面加入一次 )来维护子树

外层不论套 splay 抑或是 fhq_Treap 都可以直接对着序列来交换子树

但是最大的局限性一是维护森林将十分复杂,二是不能换根

本题树上维护 tag 即可,再附 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 的判等方式是两者的 operator:< 都不成立,那么使用 set 维护一个集合里面的维度最值和集合的大小即可

答案也就是 “最大” 的集合的大小

Luogu4426

考虑树上独立集的求法,暴力 dp 可以获得 10 pts 的好成绩

直接套到图上面不太可取,其实冲突的地方只有非树边 =mn+111

  • 我会二进制枚举!

    211 次方枚举每条边是上侧点选还是下侧点选,剩下的还是一个朴素 dp

    值得注意的是可以能有 E1 的下侧点是 E2 的上侧点,所以标记的时候要稍微复杂一点

  • Key Observation:有很多的 dp 是没有用的,同时如果不在任意一对非树边涉及的点的路径上的点的 dp 永远不变

    建立这 22 个点的虚树,别的状态的 dp 值可以提前预处理出来,剩下的 dp 照样二进制枚举做即可

时间复杂度 Θ(n+11×211)

posted @   没学完四大礼包不改名  阅读(67)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示