年终做题记录
(模板)后缀树
namespace SufT {
int ch[N<<1][29],link[N<<1],st[N<<1],len[N<<1],now,rem,cnt,sz;
void init() {cnt=1,now=1,len[0]=inf;}
int nw(int s,int l) {st[++cnt]=s,len[cnt]=l,link[cnt]=1; return cnt;}
void extend(int x) {
sz++, rem++; int last=1;
while(rem) {
while(rem>len[ch[now][s[sz-rem+1]]]) rem-=len[now=ch[now][s[sz-rem+1]]];
int &v=ch[now][s[sz-rem+1]], c=s[st[v]+rem-1];
if(x==c||!v) {
link[last]=now,last=now;
if(!v) v=nw(sz-rem+1,inf); else break;
} else {
int u=nw(st[v],rem-1);
ch[u][c]=v, ch[u][x]=nw(sz,inf);
st[v]+=rem-1, len[v]-=rem-1;
link[last]=v=u, last=u;
}
if(now==1) rem--; else now=link[now];
}
}
void creal() {
rep(i,1,cnt) if(len[i]>n) len[i]=n-st[i]+1;
}
int dfs(int u,int dep,int lf=1,int res=0) {
rep(i,0,26) if(ch[u][i]) {
int v=ch[u][i]; res+=dfs(v,dep+len[v]); lf=0;
}
if(res>1) ans=max(ans,1ll*res*dep);
else if(lf) res=1;
return res;
}
}
(模板)广义后缀树
https://www.luogu.com.cn/record/64216308
LG4198 楼房重建
全部化成斜率形式。
考虑线段树维护区间最大值 \(g_{[l,r]}\) 和区间答案 \(s_{[l,r]}\),其中区间答案为只提取这个区间,从区间左端点向右边看,能看到区间内的楼房数。
由于题目只有单点修改,所以我们唯一的问题在于如何 push up。
显然 \(g\) 是容易 push up 的,考虑 \(s\)。\(s\) 的结构比较特殊,导致其无法直接根据儿子节点算出。考虑在 pushup 的时候 \(O(\log n)\) 时间在子树中求解。
(设 \(ls\) 为左儿子,\(rs\) 为右儿子)假如我们现在在计算 \(s_u\) 的值。首先 \(s_{ls_u}\) 必然包括在 \(s_u\) 里面。我们递归处理右儿子子树中的情况:
- 设目前递归区间 \([l,r]\) (线段树上节点为 \(v\))
- 如果 \(g_{ls_v}<g_{ls_u}\),则 \(s_{ls_v}\) 不会被包含在 \(s_u\) 中,直接递归 \(rs_v\)。
- 如果 \(g_{ls_v}>g_{ls_u}\),则 \(s_{rs_v}\) 被包含在 \(s_v\) 中的那部分会有解,即对 \(s_u\) 的贡献为 \(s_v-s_{ls_v}\)。然后递归 \(ls_v\)。
https://hydro.ac/d/bzoj/record/61b40273b6d37bd6e20ce252
https://www.luogu.com.cn/record/64730443
BZOJ2588 Count on a tree
树上主席树
主席树需要维护的东西具有可减性。树上 \(u,v\) 路径为 \(s_u+s_v-s_{lca}-s_{fa_{lca}}\)。
然后每个节点的主席树可以从父节点继承过来修改。
https://www.luogu.com.cn/record/64958724
https://hydro.ac/d/bzoj/record/61b732bae2b0fa8e2f76c97b
SDOI2013 森林
考虑如何处理合并。可以启发式处理树的合并,因为只需要维护倍增数组和修改主席树即可。
https://www.luogu.com.cn/record/64866325
LG7706 「Wdsr-2.7」文文的摄影布置
考虑线段树维护区间的 \(s_i\) 表示在该区间内的答案。考虑如何合并。
- 取左区间的 \(s\) 或者右区间的 \(s\) (\(s\leftarrow \max(s_{l_i},s_{r_i})\))
- 取左区间的 \(i,j\) 右区间的 \(k\) 或左区间的 \(i\) 右区间的 \(j,k\)。维护 \(m_i\) 表示区间最大 \(a\),\(n_i\) 表示区间最小 \(b\), \(g_i\) 表示区间最大 \(a_p-b_q(p<q)\) 和 \(h_i\) 表示区间最大 \(a_p-b_q(p>q)\),则有 \(s\leftarrow \max(g_{l_i}+m_{r_i},m_{l_i}+h_{r_i})\),\(g_i=\max(g_{l_i},g_{r_i},m_{l_i}-n_{r_i})\),\(h_i=\max(h_{l_i},h_{r_i},m_{r_i}-n_{l_i})\)。
https://www.luogu.com.cn/record/64972767
LG5278 算术天才⑨与等差数列
形成差为 \(k\) 的等差数列相当于要满足以下条件:
- \(\max-\min=k(r-l)\)
- 区间的差分数组的 \(\gcd\) 为 \(k\)
- 区间内的数互不相同
第一个条件可以用线段树维护区间最大/小值,第二个条件可以用线段树维护差分数组的 \(\gcd\),第三个条件先用一个 set 维护每个数前一个与自己相等的数 \(p\),然后 \(p\) 放到线段树上维护,条件相当于区间 \(p\) 的 \(\max\) 要 \(<l\)。
注意:\(set\) 一定要用自带的 lowerbound 否则会 t!
还有求 \(n\) 个数的 \(\gcd\) 是均摊 \(O(n+\log v)\) 的,于是单点修区间 \(\gcd\) 是 \(O(n(\log n+\log v))\) 的。
https://www.luogu.com.cn/record/65080182
POI2015 LOG-Logistyka
一个美妙的结论。考虑转换“每次选出 \(c\) 个数 \(-1\) 并能选 \(s\) 次”这个条件。
我们相当于把这些数填一个 \(s\times c\) 的矩阵并且每行不同。考虑把数分为 \(\ge s\) 和 \(<s\) 的两种数。\(\ge s\) 的 \(k\) 个数最多直接填满 \(k\) 列,剩下的数可以直接从上往下摊,即出现次数 \(<s\) 的数的总和要 \(\ge s\times (c-k)\)。
https://www.luogu.com.cn/record/65201956
POI2011 ROZ-Difference
考虑枚举最多的字符 \(a\) 和最少的字符 \(b\),归并起来得到一个长 \(a+b\) 的 \(\pm 1\) 序列(\(a\) 为 \(1\),\(b\) 为 \(-1\)),我们求出包含 \(-1\) 的最大连续子序列即可。对于得到的一个极大值,如果其不含 \(-1\),在其两边找 \(-1\) 即可。
HNOI2015 开店
点分树。目前还不是特别理解,但是主体思想是根据点分治建出重心分治树,然后对于一个问询就暴力跳来得到各个子树答案之和,至于如何计算和换根有一定相似之处。
LG4178 Tree (点分治模板)
给定带权树,问距离 \(\le k\) 的点对数。
套路化地,可以分为以下几个函数:
- 找重心
- 注意找 \(v\) 子树重心时,需要记录当前连通块 \(sz\) 要设为 \(sz_v\)
- 找重心时 size 要重算
- 统计信息
- 定义点分树上的点 \(p\) 的点分数上子树 \(S_p\) ,其点分树上的父节点 \(fa_p\),则统计操作实则为统计 \(S_u\) 内部的信息。
- 实际上统计由于是统计原树,所以实际上是在原树上搜索 \(S\) 所代表的连通块来得到答案。点分树上子树必然代表原树上的一个等价连通块,点分树上子树包含关系相当于原树上的连通块包含关系。所以,本质上就是将树在 \(O(\log n)\) 层内分治为 \(size\) 之和为 \(O(n\log n)\) 的连通块。
- 遍历/建树
- 建立点分树。具体操作为在建立 \(u\) 的点分树子树时先进行统计,然后再往下建树。
这题合并分治的过程可以维护一个树状数组,然后求解即可。
https://www.luogu.com.cn/record/65628431
CQOI2015 选数
令 \(l=\lfloor \frac{L}{k}\rfloor\),\(r=\lfloor\frac{H}{k}\rfloor\),\(p(x)=\lfloor\frac{r}{x}\rfloor-\lfloor\frac{l-1}{x}\rfloor\)(代表 \([l,r]\) 中 \(x\) 倍数的个数)
我们相当于要计算 \([l,r]\) 选数的最大公因数为 \(1\) 的方案数。我们发现对于 \(d>r-l\),\(d\) 的倍数最多在 \(r-l\) 中出现一次,而出现超过一次的 \(d\) 倍数的 \(d\) 的个数是 \(O(H-L)\) 的,所以我们考虑避掉 \(d>r-l\) 的统计。
我们发现如果要是 \(d>r-l\) 的倍数,那么必须全部相等。所以我们设 \(f(x)\) 表示 \(\gcd\) 为 \(x\) 且不全相等的方案数。\(f(x)=p(x)^n-p(x)-\sum_{x|y}f(y)\),倒着统计即可,复杂度 \(O((H-L)\log (H-L))\)。
CQOI2015 网络吞吐量
最短路+网络流(点限制->拆点)的清新小模板。
注意! dijkstra 松弛的时候务必注意不能取等,不然会假。
LG3806 【模板】点分治
先把询问离线下来,然后在处理 \(u\) 点的时候,把询问全部做了,复杂度 \(O(n\log^2n+nm\log n)\)。
https://www.luogu.com.cn/record/65712878
IOI2011 Race
对于分治子块时,我们还是可以维护一个桶,然后算。