一些抄来的冷知识...
0.
一些很 sb 但是总是忘记的转换(多为数论,update lasting):
1、模改加减乘除 : \(x\%y = x-\lfloor x/y \rfloor *y\)
2、组合数的几个吸收恒等式:
3、看到个组合数考虑两种处理方法:
第一个很简单,康康能不能用二项式定理逆转成一个指数
第二个也挺简单(虽说一般来讲就是要上个板子了),组合数拆开,然后转化为 法法塔 的形式,然后卷起来
4、看到乘乘除除带指数的题目莫要慌张:
CC 是个好地方,每做一道题就能学到点东西...
一些多个数乘乘除除,甚至带指数,答案可能肥肠大的题,我们可以转 ln 之后加起来,若有指数 k 我们令原数乘 k 后累加,最后 exp 回来保留精度输出
例如 : ${x*y}\over t^k $ 我们可以变成 \(exp( ln(x)+ln(y)-ln(t)*k )\) ,算出来的结果带有一定的精度误差(但是应该不超过 1e-6 )
要注意的是, c++ 里面没有 ln 函数, 但 log 的底数默认为自然数对数 e
其次,由于是计算机内部进行牛顿迭代完成 ln 和 exp ,所以运算速度还是够快的(基本视为 O(1)?),但是如果精度要求实在变态(高精?【雾)那就只能手写牛顿迭代了吧(口胡)
5、看到一些诡异的递推处理莫要慌张:
可以先手玩一下样例,然后康康是不是有一点神奇的地方呢(好吧这种事情大家都知道的,可能是因为我是菜鸡才会碰到这种情况的时候手足无措),不过能否发现规律就不知道了...QwQ
1.
点分治 向下递归的时候两种写法(是否判断当前子节点为上层点分树中的父节点)都不会锅:
2.
枚举子集方法有三,暴力不说,枚举子集网上挺多,但复杂度...爆炸,至于另一种玄学算法总感觉很像 FWT (其实就是 FWT 里面出来的...?):
for(rint i=1;i<=m;++i)x=read(),++cnt[x];
for (int len = 2; len <= (1 << n); len <<= 1) {
for (int i = 0; i <= lim; i += len)
for (int j = i, k = i + len / 2; k < i + len; j++, k++)
cnt[j] += cnt[k];
}
3.
判断数列 \(\{a_n\}\) 中的每个数乘上一个非负系数后是否可以构成 T 时,我们可以把这些数中最小的 x 拿出来,让剩下的数和 T 满足: \(sum = T (mod x)\) ,也就是模 x 下同余,然后点 \(u\) 向模 x 意义下的 \(u+c_i\) 连一条边,长度为减去的 x 个数,然后跑最短路,康康最短距离乘上 x 是否小于等于 \(\lfloor T / x\rfloor\) 就好了
4.
若三点在某一条经过原点的直线上的投影中心对称,设三点坐标分别为 \((xa,ya),(xb,yb),(xc,yc)\),则 \((ya+yc-2 * yb,-xa-xc+2 * xb)\) 为该直线上一点,其中 B 点的投影为对称中心
5.
对于一棵树每次标记一条路径,路径上点对可相互到达,求多次标记后可相互到达的点对数,可以转化为 dfs 序,然后树剖完了线段树上扫描线,二维数点求, ZJOI 考过,然而卡常(而且我这只菜鸡也没想出来)
6.
O(1) 求 1~n 的异或和:
inline int calc(Rg int n){int t=n&3;return t&1?(t/2^1):(t/2^n);}
上面只是害人的简化写法,真实应该是这样写的:
inline int calc(Rg int n){
int t=n&3;
if(!t) return n;
if(t==1) return 1;
if(t==2) return n+1;
if(t==3) return 0;
}
可以考虑用归纳法证明
7.
O(1) GCD:
zzq 大佬的 blog 里面有写,我还搬下来做过板子...
某些 数据下会比带 log 的算法高到不知道哪里去,然鹅随机情况嘛...咳咳,我只能说,人家 O(1) 是要预处理的...
8.
O(1) 前缀 k 次和:
类似这么个式子: \(\sum_{i=0}^n i^k\) (虽说左边界是 0 是 1 没什么关系,毕竟 k 也不会等于 0 )
对于 k=1 : 原式= \((n+1)n\over 2\)
对于 k=2 : 原式= \(n(n+1)(2n+1)\over 6\) 或者 \(n(n+{1\over 2})(n+1)\over 3\) 也挺好记的
对于 k=3 : 原式= \((n+1)^2n^2\over 4\) ,其实就是 k=1 的情况平方了一下...
还有很多可以 O(1) 计算的东西,具体可以看这里
9.
对于集合 S 所有子集的异或和取值中,每种取值出现次数相等,即 S 的任意子集得到的异或和值为 x 的概率是相等的 ( x 为 S 所有子集异或和取值) 证明
10.
对于一棵 n 节点的带编号树,它的生成树数量为 \(n^{n-2}\) 个(可用 Prufer 序列证明)
11.
斐波那契数列\(f(n)=(1,1,2...)\)的前缀和数列\(S(n)\)满足:
归纳法证明:
当 n=1 时,原式成立
假设 \(S(n-1)=f(n+1)-1\) 成立,那么有:
\[S(n)=S(n-1)+f(n)=f(n+1)+f(n)-1=f(n+2)-1 \]
证毕
12.
在模 p ( p 为质数)意义下,有:
证明? 不会,背结论吧... (貌似要用唯一分解整环什么的,反正我不会...)
emmm...话说蒟蒻我好像想到了一个另类解法的开头,就是我们对于左边的式子展开,然后把除了最高项的每一项都提取出一个 \((p-1)!\) ,然后威尔逊定理搞一搞,发现这玩意儿模 p 意义下就是 p-1 ,(好像并没有什么软用,因为 p-1 并不等于 0 ),然后作者太懒不打算想,于是就咕掉了...(估计也是证不出来...)
不过还有另一种随意的解法,就是我们发现左边的式子必然是等于 0 的,然后右边的式子也等于 0 ...
(就是说左边的 x 加上 1~p-1 的所有数,总有一个是等于 0 的,而后面式子有 \(x^{p-1}-1\) ,当 x=1~p-1 时费马小定理可得原式等于 0 ,当 x=0 时前面又有个 x ...),于是等式两边恒等于 0 ? 总之记住这个诡异的性质...
13.
恒等式:
proof:
14.
一些数论变化:
(1)
proof:
n=1:
原式成立
n>1:
(2)
proof:
太迷了不会...
假设 ij 有质因子 d , d在 i 中出现 a 次, j 中出现 b 次
考虑 gcd(i,j)=1 ,那么同一个 d 不可能被 p 和 q 同时整除,于是我们分三类情况讨论取值范围:
\(d\mid p , d\nmid q\) : 这样我们发现 \({pj\over q}\) 取遍 $d^{b+1} $~ \(d^{b+a}\) 的倍数
\(d\nmid p , d\mid q\) : 这样我们发现 \({pj\over q}\) 取遍 $d^{0} $ ~ \(d^{b-1}\) 的倍数
\(d\nmid p , d\nmid q\) : 这样我们发现 \({pj\over q}\) 为 \(d^{b}\) 的倍数
(3)
proof:
其实就相当于求 1~n 每个数的因子和,变成了求每个数乘上它在 1~n 中被整除的次数
15.
无向连通图计数:
考虑令 f(n) 为 n 节点无向图个数, g(n) 为 n 节点无向连通图个数,则有:
然后...可以考虑 \(n^2\) 递推,也可以分治 FFT ,甚至还可以考虑更加高级的做法...貌似说还可以对 f 函数取个 ln 就行了?虽说验证了一下好像锅了
16.
一个 k 棵树的森林,边集为 T , 每棵树点数为 \(a_i\) ,那么包含边集 T 的生成树数量为:
proof:
Prufer 序列感性理解一下?首先每个叶子联通块连向另一个联通块,我们把连向的联通块中节点编号记录下来,那就是总共搞 k-2 次 , 并且我们每次叶子联通块连出去的节点编号并不确定,这样的 k-2 个叶子节点相乘,在枚举最后两个节点相连的那两个编号,正好是每个联通块内的节点数都乘了起来
17.
恒等式:
Proof:
正难则反,我们把后面的式子合起来:
非常暴力...
18.
恒等式,法法塔(FFT)相关:
\(\omega\) 表示单位根, \(\omega_k\) 表示 k 次单位根,则:
Proof:
分类讨论:
- \(k\nmid n\)
- \(k\mid n\)
合并一下就是原来的恒等式了
19.
自适应辛普森了解一下
20.
欧拉公式:对于一个任意维度的多面体,存在一下关系
V 表示多面体点数(Vertex)
E 表示多面体边数(Edge)
F 表示多面体面数(Flat)
proof:
从四面体开始, \(V=4,E=6,F=4\) ,结论成立
然后假设 n 面体满足公式,那么有:
然后我们发现由三个式子可以合并得到:
21.
三元环计数:
对于一个无向图,我们可以对它的边重定向,然后三元环计数
具体方法如下:
- 对于所有的边,强制度数大的点指向度数小的,度数相等则编号大指向编号小
- O n 处理所有的点,对于点 u ,让其向连出去的点 v 打上 'u' 的标记
- 对于点 u ,再次扫一遍连出去的点 v ,然后 v 去扫描连出去的点,每次碰到打过 'u' 标记的点就让 ans++
- 这样得到的 ans 就是不算重的原图三元环个数
复杂度的话是 \(O(m\sqrt m)\) , m 为边数 , n 为点数且 n、 m 同阶,下面证明统一用 m
proof:
对于每个点 v 我们分两种情况:
\(deg_v<\sqrt m\) : 这样的点最大扫描次数 O(m) ,乘上出度,复杂度 \(O(m\sqrt m)\)
\(deg_u>\sqrt m\) : 这样的点最多 $\sqrt m $ 个,所以连进来的点也最多根号个,三个根号乘起来,复杂度 \(O(m\sqrt m)\)
综上,这个算法总复杂度为 \(O(m\sqrt m)\)
22.
环的染色方案数:
我们现在要对一个环进行染色,要求相邻的点颜色不同,设点的个数为 n,颜色个数为 c,求方案数
设 \(f_i\) 表示 i 个点的染色方案数。我们可以钦定一个开头,设为 1,并顺时针一次记为 2,3,...,n ,那么有:
如果 1 号点和 n−1号点颜色不同,那么方案数为 \(f_{n-1}\),n号点可以选的方案数就是 (c−2)
如果 1号点和 n−1号点颜色相同,那么方案数为 \(f_{n-2}\),即等价于 n−2个点的环再插一个进去,此时 n号点的颜色方案数为 c−1
所以我们可以求得递推公式
初值为 \(f_0=1,f_1=c\)
根据数列的特征方程(明天要去问数学老师了,问董了的话就 update 上来),我们可以求得 \(f_n\) 的通项公式为
顺带一提,如果需要如果强制环的两个端点相同的话,方案显然就是 \(f_{n-1}\)
update:
上面讲了特征方程,这玩意儿数学老师说原理比较玄学(虽说看了看推导不是很复杂...),然后叫我自己找资料去
不过还是学到了特征方程的构造方法(不过要线性齐次递推就是了):
首先根据递推式列方程,用递推式里面每一项和最低项的位数差当做该项的次数,举个例子,如果某个数列的递推式是这样的:
那么这个数列的特征方程就是:
然后我们要做的就是解出这个方程的根(\(x_1,x_2,x_3\)),然后得到这么个通项公式:
至于这里的 A, B, C 具体值是多少就要带入数列的前 3 项解多元一次方程了...
(看您这么聪明举了这么个例子应该能搞懂了吧?)
具体特征方程是怎么来的和等比数列的转换有关,具体可以康这里
不过知乎上也有线性代数的说法(可能这个就是本质了?),给个链接就逃...(第二个答案)
23.
\(n^2\) 的 Ln 和 Exp (十分好写...)
推导如下:
Ln
求 \(F(x)=Ln(A(x))\) ,就是对于 A 求个导数求个逆然后相乘,本质就是:
我们让 \(T(x)=A(x)-1\) , 也就是丢了个常数项,那么 \(A(x)=T(x)+1\) ,且 T(x) 常数项为 0
式子中部极其...emmm,总之别看上面的式子是左右都有 F(x) ,我们可以卷积的嘛,计算某一项系数的时候只需要 O(n) 卷个 \(F(x)T(x)\) ,卷 \(0\)~\(n-1\) 就 好了,因为 T(x) 的 0 次项是 0 啊...
Exp
然后就是 Exp 了,推导很简单,求个导...
完了咱继续 O(n) 卷,这回我们就是直接对左边算 n-1 次项了,所以右边 F(x) 也卷到 n-1 次就好了,然后我们拿左边算出的 n-1 项当 F(x) 的第 n 项,至于最低位(常数项)就是 1 了...
代码仅供参考...
//by Judge
#include<cstdio>
#include<cstring>
#include<iostream>
#define Rg register
#define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
using namespace std;
const int mod=998244353;
const int M=1e5+3;
typedef int arr[M];
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[1<<21],*p1=buf,*p2=buf;
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int dec(int x,int y){return (x-=y)<0?x+mod:x;}
inline int inc(int x,int y){return (x+=y)>=mod?x-mod:x;}
inline int read(){ int x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
} char sr[1<<21],z[20];int CCF=-1,Z;
inline void Ot(){fwrite(sr,1,CCF+1,stdout),CCF=-1;}
inline void print(int x,char chr=' '){
if(CCF>1<<20)Ot();if(x<0)sr[++CCF]=45,x=-x;
while(z[++Z]=x%10+48,x/=10);
while(sr[++CCF]=z[Z],--Z);sr[++CCF]=chr;
} int n; arr A,B,inv;
inline void poly_Ln(int* A,int* B,int n){
--A[0];
fp(i,0,n-1){ B[i]=0;
fp(j,0,i) B[i]=inc(B[i],mul(A[i-j],B[j]));
B[i]=dec(mul(A[i+1],i+1),B[i]);
}
fd(i,n-1,1) B[i]=mul(B[i-1],inv[i]);B[0]=0;
}
inline void poly_Exp(int* A,int* B,int n){
fp(i,1,n-1) A[i-1]=mul(A[i],i);
A[n-1]=0,B[0]=1;
fp(i,0,n-1){ B[i+1]=0;
fp(j,0,i) B[i+1]=inc(B[i+1],mul(A[i-j],B[j]));
B[i+1]=mul(B[i+1],inv[i+1]);
}
}
int main(){ n=read(),inv[0]=inv[1]=1;
fp(i,2,n) inv[i]=mul(mod-mod/i,inv[mod%i]);
fp(i,0,n-1) A[i]=read();
poly_Ln(A,B,n);
fp(i,0,n-1) print(B[i]); sr[CCF]='\n';
poly_Exp(A,B,n);
fp(i,0,n-1) print(B[i]); sr[CCF]='\n';
return Ot(),0;
}
24.
凸多边形、凸多面体的重心计算:
多边形
对于三角形的重心计算,只需要对于三角形三个顶点 \((x_1,y_1),(x_2,y_2),(x_3,y_3)\) 进行累加除三就好了,即重心 \(G({x_1+x_2+x_3 \over 3} ,{y_1+y_2+y_3\over 3})\)
先对原图形三角剖分,然后求出每个三角形对应的重心(就用上面的三角形重心计算公式)
然后对于所有的三角形,我们对于横坐标和纵坐标分别处理,即总重心横坐标为:\(\sum_i x_i · m_i\over \sum_i m_i\) ,这里的 \(m_i\) 表示质量,学过高中物理老师可能讲过,但是在这里就代表面积了,至于总重心的纵坐标也可以用类似的方法求出,那么用 \(a_i\) 表示第 i 个三角形的坐标,总重心坐标即是:
简单的说,这里的 质量 \(m_i\) 就是面积 \(s_i\) :
多面体
类比一下,凸多面体的重心求解大概就是:
对于四面体的重心计算,我们只需要把四个顶点的横纵坐标分别加起来除去四就一一对应了重心的横坐标和纵坐标,即:\(x_G={x_1+x_2+x_3+x_4\over 4},y_G={y_1+y_2+y_3+y_4\over 4}\),四面体质量(这里即体积)的计算就各凭本事了
先对于多面体四面体剖分,然后求出每个四面体的重心
接着进行类似凸多边形的处理,把所有的坐标乘上质量,相加除去总质量即可,公式如下(和上面那个差不了多少):
其中 \(v_i\) 表示体积
PS:多嘴一句,这里的坐标其实看做多维向量就好了(学过线代的大佬们表示秒出...)
25:
求解二面角:
其实(搞信息的)并不怎么用?
要求给出两个平面求出两个平面之间的夹角(当然,两个平面相交...)
做法就是先建个系(一般来讲题目里面要给出平面的话其实就是给出了三维坐标系),然后我们要求出两个平面分别的法向量(这里的法向量也就是跟原平面垂直的一个三维向量),法向量求法如下:
设坐标\((x_0,y_0,z_0)\),然后在平面上随便找两条不相交的向量,然后两个向量叉乘就可以得到一个该平面的法向量(求法参照线性代数)
然后我们对于两个平面的法向量求个三角函数值,再反三角函数转回来就好了:
(下面那俩玩意儿就是数值相乘)
然后反三角函数直接上?
26.
点到直线距离:
两平行直线距离:
27.
曼哈顿距离 和 切比雪夫距离 的定义及相互转换:
定义
对于两个点 \((x_i,y_i),(x_j,y_j)\) ,他们的
曼哈顿距离的定义:
\[dis_{h(i,j)}=|x_i-x_j|+|y_i-y_j| \]
切比雪夫距离的定义:
\[dis_{q(i,j)}=max(|x_i-x_j|,|y_i-y_j|) \]
转化:
那么他们之间的转化有二(其实就是互相转化):
把原坐标 \((x,y)\) 转化为 \((x+y,x-y)\) ,那么原本坐标系中的曼哈顿距离就是转化后的坐标系中的切比雪夫距离
把原坐标 \((x,y)\) 转化为 \(({x+y\over 2} , {x-y\over 2})\) ,那么原本坐标系中的切比雪夫距离就是转化后的坐标系中的曼哈顿距离
我们可以发现这两个转化是互逆(等价)的,也就是说证明其中一个成立,则另一个命题也成立,那么我们不妨就证明第一个转化的正确性好了:
我们把设 原坐标为 \((x,y)\) ,新坐标为 \((x',y')=(x+y,x-y)\) ,那么有:
为了式子更加简洁,我们令 \(X=x_i-x_j,Y=y_i-y_j\)
证毕...
28.
笛卡尔定理
设四个圆的半径为: \(r_1,r_2,r_3,r_4\),
1、若四个圆两两外切,则有:
2、若前三个圆两两外切,且都内切于第四个圆,则有:
具体的证明...据说是用韦达定理?
如果是三维的五个球的话,定理也与上面类似,分为外切内切两种情况
应该还可以拓展到更高的维度...
29.
阶梯博弈:
有 n 级台阶,每级上都有一定的石子。每次可以把一个阶梯的石子往下移,0级阶梯的不能移,不能操作者输。
首先偶数层是不会有任何影响的,因为如果你移动偶数层若干个石子到奇数层,那么对手一定可以通过移动把你移的石子移到下一个偶数层,相当于这些石子仍然在偶数层。那么我们只需考虑奇数层的石子
所以我们可以把原问题变为取走奇数层的石子,无法操作者失败
那么这就是 Nim 游戏了
30.
若 \(i,j \in [1,n]\) 那么有:
一、$E(max(i,j))=O({2\over3} n) $
二、\(E(|i-j|)=O({1\over 3}n )\)
对于第一个结论,我们考虑暴力证明
对于第二个结论,我们同样考虑暴力证明
其实对于第二个结论,可以近似看做线段上随机取两个点,两点之间长度的期望值为线段总长的 \({1\over 3}\),同时这也是珂朵莉树算法的理论基础(关键数据得是随机滴...)
31.
一些多项式展开:
32.
巴什博奕
这玩意儿挺有趣的...虽说自己不会证
背景就是有 \(n\) 个石子,每次最多取 \(m\) 个,不能操作(即石子已被取光)者失败。
结论就是先手不面临 $n=k(m+1),k\in N^* $ 的情况就必胜
我们归纳一下证明:
当 \(n<=m\) 时,先手全取光,就赢了
当 \(n=m+1\) 时,先手只能取 \([1,m]\) 范围内的石子个数,剩下的石子数必然在 \([1,m]\) 范围中,这样后手就面临了第一种情况,必胜
对第二个结论推广一下,当 $n=k(m+1),k\in N^* $ 时,先手无论怎么取,剩下的石子数都是: \(k(m+1)+r,k\in N^* , r<m+1\) ,这样后手只需取走 \(r\) 个石子,情况就回到了之前的情况,这样的情况持续下去直到 \(k=1\) ,那么就变成了第二种情况,后手必胜
那么对第三个结论换个角度看看,如果说 \(n=k(m+1)+r,k\in N^* , r<m+1\) 的话,先手取走 \(r\) 个石子,回到第三种情况,先手必胜
总结一下,只要 $n\not=k(m+1),k\in N^* $ ,先手必胜,反之后手必胜
代码...这不是显然么...