伤寒杂病论

持续更新 Gu~

大概是杂文集吧

以下是目录:

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\)

\[a_x = a^{rp + q} = (a^r)^p\cdot a^q \]

这里 \(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\) .

类似快速幂,我们拆

\[a^x = a^{x\bmod k}\cdot (a^k)^{\lfloor x/k\rfloor} \]

左边预处理,右边递归做 .

这一般被叫做 \(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;
	}
};

例:程序设计分析 AC 代码(提交记录

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\),支持

  1. 区间循环移 \(x\)
  2. 区间加
  3. 单点查

一次 \(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\) 平衡树 .

posted @ 2021-12-06 19:24  Jijidawang  阅读(736)  评论(1编辑  收藏  举报
😅​