持續更新 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\) 力
這里 \(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\) 平衡樹 .