持续更新
大概是杂文集吧
以下是目录:
0. 集合
1. 光速幂(\(k\) 进制倍增)
\(a, p\) 一定,求
\[a^x \bmod p \]
1. 朴素
基本的欧拉降幂就不说了,可以将 \(x\) 缩小到 \(\varphi(p)\le p\) 的范围内 .
考虑分块预处理,令 \(r = \lfloor\sqrt p\rfloor\),则我们可以把 \(a^x\) 分成几个整块的 \(a^r\) 和一个散的
预处理 \(a^r, (a^r)^2, (a^r)^3, \cdots, , (a^r)^r\),\(a, a^2\cdots, a^r\),然后我们就可以求 \(a^x\) 力
这里 \(rp+q\) 类似一个带余除法,\(0\le q < r\) .
这样空间复杂度 \(O(\sqrt p)\),时间复杂度 \(O(\sqrt p) - O(1)\)
这玩意还可以用类似方法搞搞扩域光速幂,矩阵光速幂啥的 .
Code:
template<int MOD>
struct FastPow
{
private:
ll p1[66666],p2[66666];
static ll qpow(ll a,ll n)
{
ll ans=1;
while (n)
{
if (n&1) ans=ans*a%MOD;
a=a*a%MOD; n>>=1;
} return ans%MOD;
}
public:
explicit FastPow(ll a)
{
ll t1=a,t2=qpow(t1,65536); p1[0]=p2[0]=1;
for (int i=1;i<65536;i++) p1[i]=p1[i-1]*t1%MOD;
for (int i=1;i<65536;i++) p2[i]=p2[i-1]*t2%MOD;
}
ll operator()(unsigned n){return p2[n>>16]%MOD*p1[n&65535]%MOD;}
};
例题:块速递推
2. 不朴素
我们这个 \(r\) 也不一定取 \(\sqrt p\) .
类似快速幂,我们拆
左边预处理,右边递归做 .
这一般被叫做 \(k\) 进制倍增,当 \(k=2\) 时等价于快速幂 .
这个可以用来调节预处理复杂度和询问复杂度,例如 \(r\) 取 \(p^{1/3}\) 时 .
空间复杂度 \(O(k\log_k p)\) 时间复杂度 \(O(k\log_k p) - O(\log_k p)\)(有错望指正).
例题:能量采集
2. 在线离散化
就是一个小工具 .
一个全自动 Hash 器,挺好用,可以让你避免离散化化化化化化
template<typename T>
struct pool
{
unordered_map<T, unsigned> pol;
unsigned cc = 0;
unsigned get(T x)
{
auto ptr = pol.find(x);
if (ptr == pol.end()){pol[x] = ++cc; return cc;}
else return ptr -> second;
}
};
using namespace std;
const int N = 1e6 + 500;
typedef long long ll;
int fa[N], n;
inline void init(int n){for (int i=0; i<=n; i++) fa[i] = i;}
int fnd(int x){return (fa[x] == x) ? x : fa[x] = fnd(fa[x]);}
void merge(int u, int v){fa[fnd(u)] = fnd(v);}
struct pool
{
unordered_map<int, int> pol;
int cc = 0;
int get(int x)
{
auto ptr = pol.find(x);
if (ptr == pol.end()){pol[x] = ++cc; return cc;}
else return ptr -> second;
}
}T;
void _()
{
init(1e6);
scanf("%d", &n);
vector<pair<int, int> > query;
for (int u, v, opt, i = 0; i<n; i++)
{
scanf("%d%d%d", &u, &v, &opt);
if (opt == 1)
{
int U = T.get(u), V = T.get(v);
merge(U, V);
}
else query.push_back(make_pair(u, v));
}
for (auto x : query)
{
int u = x.first, v = x.second, U = T.get(u), V = T.get(v);
if (fnd(U) == fnd(V)){puts("NO"); return ;}
} puts("YES");
}
int main()
{
int T; scanf("%d", &T);
while (T--) _(); // solve
return 0;
}
3. 邻接表
好像目前没啥应用
就是把邻接表链的链表改成别的数据结构,比如改成平衡树 / SkipList 就可以支持删边 .
4. C-Style 字符串小结
常见函数
strlen
长度strcmp
比较(strcmpi
不区分大小写)strcpy
复制strcat
追加strrev
字符串反转strupr
转大写
全文读取:
void rdfile(const char* a)
{
char* ptr=a;
while ((*ptr=getchar()) && (*ptr!=EOF)) ++ptr;
}
5. 信息传递的一点研究
0. 一些小前置
\(\mathsf D_1\) 基环树是啥?link . 基环内向树是啥?所有边的方向都向着环的方向的基环树 .
\(\mathsf D_2\) 弱联通是啥?有向图去掉方向后的连通性
\(\mathsf P\)
维护一个序列 \(a\),支持
- 区间循环移 \(x\) 位
- 区间加
- 单点查
一次 \(1\) 操作拆成三次区间 reverse,用文艺平衡树即可 \(O(\log n)\),于是 \(3\) 可以用 tag 做到 \(O(\log n)\),\(2\) 可以递归 \(O(\log n)\)
1. 朴素信息传递
题面大概是
给一个基环内向树森林求最小环
仨经典算法
- 并查集
- 拓扑排序
- tarjan
一些别的算法
- dfs,用并查集标记弱联通
- Floyd 判圈法
2. 带修信息传递
题面大概是
给一个基环内向树森林求最小环
每次修改将 \(u\to v_0\) 的边删掉,改成 \(u\to v_1\) .
现在除掉方向 .
从环上按顺序把每棵树的 DFS 序拼起来就当作整个森林的 DFS 序 .
维护 DFS 序的过程就是前置 \(\mathsf P\),区间加维护每个点到环的距离,就可以维护环长了 .
预处理 \(O(n)\),修改 \(O(\log n)\),哈哈 .
或许可以 Euler Tour Tree??
假了请告诉我并爆 D 我的算法 qwq
upd: 这玩意其实叫 深度确定问题 .
upd:不是 depth determination,不能并查集,只能 \(\log\) 平衡树 .