2021-03 杂题泛做
Codeforces 1100F
又是维护线性基的 \(trick\),考虑使用扫描线回答询问
在插入线性基的过程中给每一位存上个 \(position\) 最大的 \(1\) 就行了
因为是贪心,希望 \(l\) 大的询问也可以用到这个 \(1\)
sequence
考虑每个序列的 \(And\) 和最多变化 \(log\) 次
用 \(ST\) 表维护区间 \(And\) 和,然后逐段二分得到修改的点
剩下的扫描线套线段树,线段树上的操作是 \(\texttt{耐(ruo)人(zhi)寻(dao)味(jia)}\) 的,值得思考
uoj21
考虑如何求 \(\max(\sum\frac{a_i(x-1)}{x})\)
想到的一个做法是枚举有意义的 \(x\),但是这种 \(x\) 很多
但是不难观察到相邻的两个有意义的数之间贡献是直接 \(+1\) 的,差分就能维护了
但是这样需要整除分块来进行加减
每个数上的答案是显然可以快速统计的,那么 \(O(n\ln n)\) 即可
uoj22
先把序列从大到小排一下,因为先模后面再前面没有意义
第一问:\(dp_{i,j}\) 表示前 \(i\) 个数,\(j\) 可否为当前可行答案
从当前向下一个转移,最后从 \([0,a_n-1]\) 选答案,实现的时候数组甚至可以一维
按照上面的思路,设 \(f_j\) 表示当前答案为 \(j\) 的方案数,转移找一个 \(\le j\) 的数即可
显然转移的时候 \([j\% a_i,j-1]\) 的 \(a_i\) 是可以任意插到排列里面的,预处理排列数即可
值得注意的是在计算的开始 \(f[x]\) 要把大于 \(x\) 的 \(a_i\) 插入到排列里面
uoj23
主思想是每个边当作 \(x\) 的话长度为 \(L\) 的边的数量就是 \([x^L]f(x)\),以前见过,这次加深了印象
考虑环上点的贡献:对于一个点如果到环顶的距离分别为 \(a,b\) 考虑每个边是 \(x^1\) 的话就是链顶 \(P(x)\times (x^a+x^b)\)
写的时候和一般的仙人掌差不多,就是先跑最短路,然后找环,\(50’\) 的做法就是暴力维护多项式
值得注意的一点是记得维护环的几个链的重边的乘积
\(70\) 做法把每个环上的贡献算一算,然后对环的部分分治乘法就行了,每个节点扔一个站位的 \(x^0\) 把前面的答案收集一下
\(\Theta(n\log^3 n)\) :考虑点分治
对于仙人掌,如法炮制树上点分治的做法,使断开与其相邻的边之后子联通块的大小尽量小
重心连出去的边分为向上和向下,可以快速得到父链多项式,对于向下的边求子树的加和多项式,进行卷积即可,向上的直接加入答案
如果向上的边是个环,需要对环上的点都处理一下
仙人掌点分使用圆方树,每个方点上放 \(SZ\) 环的权值,求带权重心即可
这个做法代码不很会写,先鸽掉,还有俩 \(\log\) 的做法,也不会
uoj31
如果当前序列不合法,那么必然是一个前缀和为负,那么找到后面最近的一个 \(1\) 翻过来就行
实现需要维护所有 \(1\) 的位置和前缀和,分别使用单调指针扫和数据结构维护即可
建议树状数组,因为线段树被卡常了
其实貌似没必要用数据结构,维护一个变量就行了,我思维僵化了
uoj33
本来对这个 \(gcd(i,j)=k\) 想的是反演,其实是复杂了
如果能求到 \(ans[i]\) 表示 \(gcd\) 为 \(i\) 的倍数的数的个数,最后套上一个 \(\Theta(n\ln n)\) 的容斥可以得到原答案
对原树跑点分治,值得指出的是这个树是有根的,对于当前分治重心向上的子联通块需要特殊处理
-
都在分治重心 \(x\) 子树点对,维护一个表示深度的桶和一个深度的倍数,然后合并答案
-
对于一个在父联通块,一个在子树里面的点对
显然对于父链上的点还是可以处理如上的两个桶,对于 \(ans[i]\) 的增量应当为两个倍数桶的乘积
对于子树的倍数桶要求开头不是 \(0\) 的部分的和,需要大量记录,空间不能接受
仍旧使用根号分治,对于 \(\ge \sqrt n\) 的部分暴力,预处理答案即可(也可以说是记忆化吧)
异或图
图状压下来之后只有 \(55\) 位,在一个线性基的射程范围之内
\(dfs\) 枚举哪些数放到了一个集合里面,那么不在同一个集合里面的边不能存在,所以可以建一个异或方程组的线性基
线性基里面的异或结果不能存在,那么得到 \(res=2^{n-num}\)
把结果累加到 \(f_{cnt}\) 里面,最后使用斯特林反演即可
关于高斯消元解异或方程组,解的个数是把剩下的全消成 \(0\) 的时候 \(2\) 的剩下的行数次方
uoj32
直接对 \(1\) 所在的联通块考虑,题目求的也就是路径上不存在负环的 \(x\) 的个数
如果出现了一个负环,那么影响的一定是它的后继的答案区间,所以先 \(floyd\) 传递闭包找到联通性
考虑 \(Bellman-Ford\),\(f[n][i][j]\) 表示环上的 \(col\) 的和为 \(j\) 时,在点 \(i\) 处走了 \(j\) 步的最短路
最短路一定是简单路径,那么如果 \(f[n][i]>f[n-1][i]\) 说明存在一个负环
这里不考虑 \(k\) 列出来的式子如上,那么考虑上 \(k\) 就好说了:
如果满足该式子,那么当前的 \(x\) 符合条件
对于上面的不等式,枚举所有的 \(k\),得到答案之后求交集,拆掉一个 \(\min\{\}\)
剩下的一个也跟着枚举 \(j\) ,做出来之后求并集即可
实现的时候我完全反过来写的,先交后并,最后总方案容斥
值得指出的是,实现的时候可以把 \(\inf\) 设成 \(2\times10^{18}\),会方便一些
uoj48
直接把第一个数分解,然后后面的数一个一个对着质因子分,除掉最小的一个
uoj49
对于当前的 \(s\) 选择箱子的手段一定是在两边找距离最小的一个,同时选择的起始点一定有箱子
关于第二个结论直接展开所用的时间的式子,是一个绝对值方程,这个东西显然有奇数个箱子的话是中位数,偶数个箱子放到左右两边答案一样
显然的二分答案 \(mid\),然后枚举每个 \(x_i\) 作为中位数所在的位置
设当前区间 \([l,r]\) 满足这个坐标是中位数所在的位置,统计遍历这些区间所使用的时间,这个通过前后缀和能做到 \(O(1)\)
由于 \(x_i\) 位置本身可能有箱子,那么其实这样的 \([l,r]\) 可能有多个,但是当 \(x_i\) 递增的时候这些区间必然是单调右移的
证明可以考虑比较 \(x_{i-1}\) 的尾状态和 \(x_i\) 的初始状态
尾状态的 \(l\) 到 \(x_{i-1}\) 中有 \(\frac{mid}2\) 个数,这样是满足下一个中位数点的初始状态的,证毕
一道难题实现一定是大关,所以它很难写
其实可以维护 \(lnow,rnow\) 两个变量表示左右边界的取用数量,更新区间的时候快速增量,也是可以保证复杂度的正确性的
uoj50
把式子捋一下:
然后设:
那么把组合数展开之后得到
那么再傻也能给看出来 \(f(n)\) 和 \(g(n)\) 都是卷积,先硬上 \(cdq\_NTT\)
但是直接写还是过不了样例,那么问题出在了一种叫做 \(\texttt{我卷我自己}\) 的卷积模型上面
也就是说对于 \([1,mid]×[l,mid]\) 的贡献在分治区间左端点为 \(1\) 的时候无法进行正确贡献
那么转移 \(G\) 的时候处理的长度为 \(r-l+1\) 的数组中,如果 \(i<l\) 那么放 \(2F[i]\) ,\(l\le i\le mid\) 放 \(F[i]\) 反之放 \(0\) 就行
即如下:
inline void solve(int l,int r){
if(l==r){if(l==1) F[1]=1; else F[l]=mul(F[l],mul(i2,fac[l-1])); return ;}
int mid=(l+r)>>1; solve(l,mid);
int lim=1; while(lim<=(r-l+1+mid-l+1)) lim<<=1;
for(reg int i=0;i<=r-l;++i)
if(i<l) H[i]=mul(F[i],mul(2,ifac[i]));
else if(i<=mid) H[i]=mul(F[i],ifac[i]);
else H[i]=0;
For(i,0,mid-l) I[i]=mul(F[i+l],ifac[i+l]);
for(reg int i=mid-l+1;i<=r-l;++i) I[i]=0;
for(reg int i=r-l+1;i<lim;++i) H[i]=I[i]=0;
NTT(H,lim,1); NTT(I,lim,1);
for(reg int i=0;i<lim;++i) H[i]=mul(H[i],I[i]);
NTT(H,lim,-1);
for(reg int i=mid+1;i<=r;++i) G[i]=add(G[i],H[i-l]);
for(reg int i=0;i<=r-l;++i) H[i]=mul(A[i],ifac[i]);
for(reg int i=0;i<=mid-l;++i) I[i]=G[i+l];
for(reg int i=mid-l+1;i<=r-l;++i) I[i]=0;
for(reg int i=r-l+1;i<lim;++i) H[i]=I[i]=0;
NTT(H,lim,1); NTT(I,lim,1);
for(reg int i=0;i<lim;++i) H[i]=mul(H[i],I[i]);
NTT(H,lim,-1);
for(reg int i=mid+1;i<=r;++i) F[i]=add(F[i],H[i-l-1]);
return solve(mid+1,r);
}
uoj51
考虑 \(\sqrt n\) 不大,\(2^{31}\) 必然大于 \(10^9\)
那么状态并不多,考虑记录每个状态的 \(SG\) 函数即可,使用记忆化搜索实现这个算法
uoj52
挺 \(\mathrm{ codeforces}\) 的题呀
直接取阀值 \(x=\lfloor\frac{k}3\rfloor\),在 \(a,b,c\) 三个数组里面找到这个位置上的数
最小的那个数组里面前面的数不可能作为答案,删掉,迭代下去即可
注意:如果把 \(\texttt{kth.cpp}\) 已经挪到桌面上了就用桌面上的写
uoj53
有一个显然的 \(\Theta(nk\log(nk))\) 的做法
但是这个做法有很多冗余,也就是把所有的点都放到堆里面,其实我们只关注最小的
关注到这个 \(a,b,c\) 的路径上的点可以被划分出来三个链,那么我们可以每次取最小的进行扩展
这个过程是十分有技巧的,也就是记录链的首尾,然后进行最值分治
形式化的而言,考虑当前链为 \((st,ed,pos)\),\(pos\) 表示链上最小权所在的点
那么可以往堆里面扔一个 st,fa[pos],ask_chain(st,fa[pos])
,和 unson(pos,ed),ed,ask_chain(unson(pos,ed),ed)
其中 \(\texttt{underson}\) 表示该方向的儿子,ask_chain
表示链上最小值
这样的话每次扩战展放到堆里面的数是常数级别的,就可过了
拆链想到了,这个分治感觉有点意思,实现没有难度
值得一提的是,这个题让我理解了 \(\texttt{zkw线段树}\),这样的收获十分有意义
另外:记录本题卡空间的做法
\((1)\) 用手写堆代替 \(\texttt{priority\_queue}\)
\((2)\) 记录堆里面的最大元素,\(\texttt{k<q.size()}\) 那么就不再加入比最大还大的部分了
\((3)\) 重复使用数组,比如原来的 \(sz\) 数组后面就没用了
\((4)\) 结构体不要使用 \(\texttt{node(st,ed,pos,val)}\) 来赋初值,因为这个函数需要空间
uoj207
首先学习一下如何使用 \(\texttt{LCT}\) 维护虚子树信息
值得一记的是 \(\texttt{make\_root}\) 的翻转时一定保证当前的 \(x\) 没有右儿子
考虑给每个路径赋一个权值,加到首尾上面,然后对于每个路径,如果以 \(x\) 为根,\(y\) 的子树里面的权值和恰等于现在的路径权值和,那么满足条件
考虑维护子树异或和,如果等于当前所有的路径异或和,那么正确
这里用 \(2^x\) 次方对大质数的模当权值比较靠谱
SDOI2018 旧试题
先把式子推一推:
直接上连暴力都拿不到,关注到这个 \(\mu(i)\) 有贡献的不多,看起来很像一个三元环计数的模型
那么就可以对全不同,两个一样,全一样的三部分分开计数
考虑系数中是轮换对称的三个独立函数,也就是
显然可以 \(\Theta(n\ln n)\) 的时间复杂度之内求出来,那么就做完了
ARC115 D. Odd Degree
答案显然是把所有的联通块的答案搞个分治乘法
因为奇偶性的缘故,有奇数度数的点一定只有偶数个,因为总度数是 \(2n\) 为偶数
对于一个有偶数个度数为奇数的生成树,钦定哪个子集为奇数度数,剩下为偶数度数,都是可以从叶子开始向上剥来得到
具体就是如果度数不为其儿子的情况剥完了,如果留下的边数是偶数,就断掉祖先边
那么对于奇数个数为 \(k\in\{even\}\) ,方案是 \(\binom nk\)
所以就做完了
ARC115 E.LEQ and NEQ
考虑容斥来做:总方案减掉有相邻且相同的段的方案
那么有
第二维就直接实现了容斥,答案为 \(f_{n,0}-f_{n,1}\)
用单调栈优化 \(dp\) 并使用若干个前缀和数组分别维护其奇偶位置上的前缀和即可
还有一种相对简单的转移,不需要第二维
发现最后是 \(f[n][0]-f[n][1]\) 那么可以把对 \(f[n][0]\) 最后贡献的直接置负,最后加起来
Codeforces1119H
基础想法就是挨着个卷,优化考虑 \(FWT\) 的点值式子:
那么不难发现这个 \(x,y,z\) 前面的系数只有 \(8\) 种
一个 \(trick\) 是将 \(b_k,c_k\) 异或 \(c_k\) 最后的答案再异或回来所有的 \(a_i\)
那么还有 \(4\) 种需要考虑的情况,利用四种情况的和为 \(n\),仅考虑 \(F_k[b/c/(b\ \mathrm{xor}\ c)[i]=0\) 分别搞出来四个方程,手动解出来
最后就剩下快速幂,\(\mathrm{IDWT}\) 和下标反映射了
loj6401
因为是一个 \(SAM\) 题,那么考虑每个点上有多少字符串能被覆盖
那么使用单调指针扫一下得到每个点的最长合法后缀的长度,然后放到 \(SAM\) 上
最后在后缀树上扫一下,用每个点的儿子来更新 \(fa\) ,实现的时候注意两点:
\((1)\) 基数排序:for(reg int i=tot;i>=1;--i) id[ton[len[i]]--]=i;
\((2)\) 每个点的答案不仅要让 \(\max\) 和长度取 \(\min\) ,还得注意和 \(0\) 取 \(\max\)
uoj60
没有任何一种方案比 \(4\times 3\times 3 \times 3\times \dots\) 更大,直接输出即可
loj6031
先把这个 \(\mathrm{w(l,r)}\) 看明白:是指整个子串而非这个区间里面的所有子串
对 \(S\) 建自动机,得到每个点的 \(\mathrm{endpos}\) 集合大小
对于每个 \(w\) 放到上面跑匹配,能求出来 \(w\) 中以每个下标为结尾是 \(S\) 子串的最长后缀
对于 \([a,b]\) 中的每个区间,在后缀树上跑倍增,复杂度 \(\Theta(\mathrm{qmlog\ n})\)
那么复杂度瓶颈在每个询问都得跑,考虑使用根号分治
-
对于 \(k\ge \sqrt n\) 的部分,询问不超过 \(\sqrt n\) 个,直接照着上面的做法实现即可
-
对于 \(k\le \sqrt n\) 的部分,对所有 \(w_i\) 对应的 \(a_i,b_i\) 离线之后扔一个莫队维护每个区间的出现次数
求完了之后就直接 \(\Theta(k^2)\) 做就行了
复杂度想错了好久,其实是 \(\Theta(nk)\) 的
DZY loves Chinese II
先求出来一个原图的生成树,对于非树边,随机赋一个权值
树边的权值就是非树边路径经过它的路径权值的异或和
询问直接判断是不是线性不相关就行了
正确性?
如果分成两个联通块,中间树边有且仅有一条,且权值一定是同样连接两个点集的非树边的权值的异或和
shallot
线性基的删除是有通法的,但是这里考虑使用线段树分治,那么就分治就好了