- 數據結構
- 線段樹
- 樹鏈剖分 √
- Treap
- Splay
- 左偏堆
- 劃分樹 待添加
- 字符串
- KMP
- MANACHER
- 字典樹
- 01字典樹 √
- AC自動機 √
- 後綴數組 √
- 迴文樹 √
- 數論
- 矩陣快速冪 待添加
- 圖論
- 最短路(Dijkastra)待添加
- 最短路(Floyd)待添加
- 最短路(SPFA)待添加
- 最小生成樹(Prim) 待添加
- 最小生成樹(Kurskal) 待添加
- 計算幾何
- 掃描線 √
- 其他
- 大整數模板 √
- 快速輸入模板 (整數) √
- 快速輸入模板 (實數) √
- 1~MOD的逆元 √
-
樹鏈剖分 √
const int maxn = 100000 + 5; //樹中節點個數
//邊集
struct Edge{
int v,pre,c;
}Es[maxn * 2];
int head[maxn],TOT_EDGE;
void INIT_EDGE(){ //邊集初始化
memset(head,-1,sizeof head);
TOT_EDGE = 0;
}
void ADD_EDGE(int u,int v,int c){ //添加邊
Es[TOT_EDGE].v = v;
Es[TOT_EDGE].c = c;
Es[TOT_EDGE].pre = head[u];
head[u] = TOT_EDGE++;
}
/**樹鏈剖分部分*/
/**
** son: 節點重兒子
** fa: 節點父節點
** dep: 節點在樹中深度
** size: 節點子樹的大小
** top: 節點在重鏈中的深度最低節點(最高節點)
** tid: 節點編號(hash值)
** RANK: RANK[v]編號爲v的是第RANK[v]節點
** SegSize:用於編號的迭代輔助變量,最小編號爲SegSize的初始值
*/
int son[maxn],fa[maxn],dep[maxn],size[maxn];
int top[maxn],tid[maxn],RANK[maxn],SegSize;
int DFS(int rt){
dep[rt] = dep[fa[rt]] + 1;
son[rt] = 0;
size[rt] = 1;
for(int i = head[rt];~i;i = Es[i].pre){
int v = Es[i].v;
if(v != fa[rt]){
fa[v] = rt;
size[rt] += DFS(v);
if(size[son[rt]] < size[v]) son[rt] = v;
}
}
return size[rt];
}
void Split(int rt,int tp){
top[rt] = tp;
tid[rt] = ++SegSize;
RANK[tid[rt]] = rt;
if(son[rt]){
Split(son[rt],tp);
for(int i = head[rt];~i;i = Es[i].pre){
int v = Es[i].v;
if(v != fa[rt] && v != son[rt]) Split(v,v);
}
}
}
void TreeLineSplit(){ //開始剖分
dep[0] = 0;
size[0] = 0;
fa[1] = 0;
DFS(1);
SegSize = -1;
Split(1,1);
}
Treap
/**
** ch:節點的孩子節點
** fa:夫節點
** fix;優先級
** size:當前子樹的大小
** tot:節點個數(包括sroot)
** sroot:超級根節點
** key:節點的鍵值
*/
const int maxn = 15000 + 50,INF = 0x7fffffff;
int ch[maxn][2],fa[maxn],fix[maxn],size[maxn];
int tot,root,sroot;
int key[maxn];
/**
** Treap初始化
*/
void Treap_Init(){
tot = 1;
root = sroot = 0;
ch[sroot][0] = ch[sroot][1] = 0;
fa[sroot] = 0;
size[sroot] = 0;
fix[sroot] = key[sroot] = INF;
}
/**
** 新建一個父節點爲FA,鍵值爲k的節點,返回節點編號
**
*/
int NewNode(int FA,int k){
ch[tot][0] = ch[tot][1] = 0;
fa[tot] = FA;
size[tot] = 1;
fix[tot] = rand();
key[tot] = k;
return tot++;
}
/**
** 更新rt的size信息
*/
void PushUp(int rt){ size[rt] = size[ch[rt][0]] + size[ch[rt][1]] + 1; if(rt == sroot) size[rt] = 0;}
/**
** 旋轉操作
** kind: 0.表示rt爲fa[rt]的左孩子 進行右旋操作 zig
** kind: 1.表示rt爲fa[rt]的右孩子 進行左旋操作 zag
*/
void rotate(int rt,int kind){
int prt = fa[rt];
ch[prt][kind] = ch[rt][!kind];fa[ch[rt][!kind]] = prt;
ch[fa[prt]][ch[fa[prt]][1] == prt] = rt;fa[rt] = fa[prt];
ch[rt][!kind] = prt;fa[prt] = rt;
PushUp(prt);PushUp(rt);
}
/**
** 在根爲rt的樹中插入鍵值爲K的元素 這裏可以重複鍵值
*/
void Insert(int rt,int k){
while(ch[rt][k >= key[rt]]) rt = ch[rt][k >= key[rt]];
ch[rt][k >= key[rt]] = NewNode(rt,k);
rt = ch[rt][k >= key[rt]];
while(fa[rt] != sroot && fix[rt] > fix[fa[rt]]) rotate(rt,ch[fa[rt]][1] == rt);
if(fa[rt] == sroot) root = rt;
while(fa[rt] != sroot){
rt = fa[rt];PushUp(rt);
}
}
/**
** 刪除鍵值爲k的節點,使用前要保證一定有鍵值爲k的節點
*/
void Delete(int rt,int k){
if(key[rt] == k){
if(ch[rt][0] && ch[rt][1]){
int nrt = ch[rt][0];
if(fix[nrt] < fix[ch[rt][1]]) nrt = ch[rt][1];
rotate(nrt,ch[rt][1] == nrt);
Delete(rt,k);PushUp(nrt);
}
else{
int nrt = ch[rt][0];
if(nrt == sroot) nrt = ch[rt][1];
ch[fa[rt]][ch[fa[rt]][1] == rt] = nrt;fa[nrt] = fa[rt];
}
}
else{
Delete(ch[rt][k >= key[rt]],k);
PushUp(rt);
}
}
/**
** 在rt的樹中查找鍵值第k小的節點 返回節點編號
*/
int Search(int rt,int k){
if(k <= size[ch[rt][0]]) return Search(ch[rt][0],k);
else if(k > size[ch[rt][0]] + 1) return Search(ch[rt][1],k - 1 - size[ch[rt][0]]);
else return rt;
}
/**
** 查找鍵值爲k的節點在樹中是第幾小的
*/
int getRank(int rt,int k){
if(rt == sroot) return 0;
if(k >= key[rt]) return size[ch[rt][0]] + 1 + getRank(ch[rt][1],k);
else return getRank(ch[rt][0],k);
}
Splay
/** ---- SPLAY ---- **/
/** ch 邊
** fa 父節點
** size 子樹大小
** key 關鍵字大小
** tot 總共節點的個數
** root 當前的樹根
** sroot 超級根節點
**/
int ch[maxn][2],fa[maxn],size[maxn];
int key[maxn],pos[maxn];
int tot,root,sroot;
/** 初始化 */
void Splay_Init(){
tot = 1;
root = sroot = 0;
ch[0][0] = ch[0][1] = 0;
fa[0] = 0;
key[0] = INF;
size[0] = 0;
}
/** 新建一個節點 */
int NewNode(int FA,int k,int p){
ch[tot][0] = ch[tot][1] = 0;
fa[tot] = FA;
size[tot] = 1;
key[tot] = k,pos[tot] = p;
return tot++;
}
/** 更新當前子樹的大小 和 根節點保留的信息 */
void PushUp(int rt){size[rt] = 1 + size[ch[rt][0]] + size[ch[rt][1]];}
/** 節點的旋轉操作
** kind 0 表示rt爲fa[rt]的左孩子
** kind 1 表示rt爲fa[rt]的右孩子
** kind 0 進行zig 1 進行zag
*/
void rotate(int rt,int kind){
int prt = fa[rt];
ch[prt][kind] = ch[rt][!kind];fa[ch[rt][!kind]] = prt;
ch[fa[prt]][ch[fa[prt]][1] == prt] = rt;fa[rt] = fa[prt];
ch[rt][!kind] = prt;fa[prt] = rt;
PushUp(prt);PushUp(rt);
}
/** 將rt節點 伸展至goal的孩子節點 */
void Splay(int rt,int goal){
while(fa[rt] != goal){
if(fa[fa[rt]] == goal) rotate(rt,ch[fa[rt]][1] == rt);
else{
int prt = fa[rt],kind = (ch[fa[prt]][1] == prt);
if(ch[prt][kind] == rt) rotate(rt,kind),rotate(rt,kind);
else rotate(rt,!kind),rotate(rt,kind);
}
}
if(fa[rt] == sroot) root = rt;
}
/** 在根爲rt的樹內,插入關鍵字爲k的節點 */
void Insert(int rt,int k,int p){
while(ch[rt][k > key[rt]]) rt = ch[rt][k > key[rt]];
rt = (ch[rt][k > key[rt]] = NewNode(rt,k,p));
Splay(rt,sroot);
}
/** 在根爲rt的樹內,查找關鍵字爲k的節點 */
int Find(int rt,int k){
while(ch[rt][k >= key[rt]])
if(k == key[rt]) return rt;
else rt = ch[rt][k >= key[rt]];
return sroot;
}
/** 查找關鍵字爲k 的前驅 */
int Find_Pre(int rt,int k){
rt = Find(rt,k);
Splay(rt,sroot);
if(ch[rt][0] == sroot) return sroot;
else rt = ch[rt][0];
while(ch[rt][1]) rt = ch[rt][1];
return rt;
}
/** 查找關鍵字爲k 的後繼 */
int Find_Next(int rt,int k){
rt = Find(rt,k);
Splay(rt,sroot);
if(ch[rt][1] == sroot) return sroot;
else rt = ch[rt][1];
while(ch[rt][0]) rt = ch[rt][0];
return rt;
}
01字典樹 √
const int maxn = 100000 + 5; //集合中的數字個數
typedef long long LL;
int ch[32 * maxn][2]; //節點的邊信息
LL value[32 * maxn]; //節點存儲的值
int node_cnt; //樹中當前節點個數
inline void init(){ //樹清空
node_cnt = 1;
memset(ch[0],0,sizeof(ch));
}
inline void Insert(LL x){ //在字典樹中插入 X
//和一般字典樹的操作相同 將X的二進制插入到字典樹中
int cur = 0;
for(int i = 32;i >= 0;--i){
int idx = (x >> i) & 1;
if(!ch[cur][idx]){
memset(ch[node_cnt],0,sizeof(ch[node_cnt]));
ch[cur][idx] = node_cnt;
value[node_cnt++] = 0;
}
cur = ch[cur][idx];
}
value[cur] = x; //最後的節點插入value
}
inline LL Query(LL x){ //在字典樹中查找和X異或的最大值的元素Y 返回Y的值
int cur = 0;
for(int i = 32;i >= 0;--i){
int idx = (x >> i) & 1;
if(ch[cur][idx ^ 1]) cur = ch[cur][idx ^ 1];
else cur = ch[cur][idx];
}
return value[cur];
}
KMP
const int maxn = 100;
char s[maxn],p[maxn];
int fail[maxn];
/* p爲模式串
** f爲保存失配邊的數組
**
*/
void getFail(char* p,int* f){
int m = strlen(p);
f[0] = 0; f[1] = 0;
for(int i = 1;i < m;++i){
int j = f[i];
while(j && p[i] != p[j]) j = f[j];
f[i + 1] = p[i] == p[j] ? j + 1 : 0;
}
}
/*
** s爲文本串 p爲模式串,fail保存失配邊
** 匹配成功返回true
** 失敗返回false
*/
bool Match(char* s,char* p,int* fail){
getFail(p,fail);//得到fail數值
int m = strlen(p),n = strlen(s);
int i = 0, j = 0;
while(i < n){
while(i < n && j < m && s[i] == s[j]){//新一輪匹配
i++,j++;
}
if(j == m) return true;//匹配成功
j--;
while(j && p[j] != s[i]) j = fail[j];//根據fail跳轉
}
return false;
}
AC自動機 √
const int maxn = 10000 * 50 + 50,sigma_size = 26;
/** AC_AUTOMATON */
int ch[maxn][sigma_size],fail[maxn],tot;
int value[maxn];
/** 初始化 */
void Init(){
memset(ch[0],0,sizeof ch[0]);
tot = 1;
fail[0] = -1;value[0] = 0;
}
/** 插入字符串 s */
void Insert(char* s){
int idx,cur = 0;
while( *s ){
idx = *s - 'a';
if(ch[cur][idx] == 0){
memset(ch[tot],0,sizeof ch[tot]);
fail[tot] = 0;
value[tot] = 0;
ch[cur][idx] = tot++;
}
cur = ch[cur][idx];
s++;
}
value[cur]++;
}
/** GetFail */
void GetFail(){
queue<int> Q;
Q.push(0);
fail[0] = -1;
int cur,idx,f;
while(!Q.empty()){
cur = Q.front();Q.pop();
for(int i = 0;i < sigma_size;++i){
if(ch[cur][i]){
f = fail[cur];
while(f != -1 && ch[f][i] == 0) f = fail[f];
fail[ch[cur][i]] = (f == -1) ? 0 : ch[f][i];
Q.push(ch[cur][i]);
}
else{
f = fail[cur];
ch[cur][i] = (f == -1) ? 0 : ch[f][i];
}
}
}
}
/** 以上具有通用性 */
/** 匹配 */
int Search(char *s){
int cur = 0,idx,ret = 0,tmp;
while(*s){
idx = *s - 'a';
tmp = cur = ch[cur][idx];
if(value[tmp]) while(tmp){
ret += value[tmp];value[tmp] = 0;
tmp = fail[tmp];
}
s++;
}
return ret;
}
MANACHER
const int maxn = 500;
int dis[maxn];
char str1[maxn],str2[maxn];
int get_dis(){
int len = strlen(str1);
str2[0] = '$';
char* str_a = str2 + 1;
for(int i = 0;i <= len;++i){
str_a[i * 2] = '#';
str_a[i * 2 + 1] = str1[i];
}
int id = 0, mx = 1,len2 = strlen(str2);
for(int i = 1;i < len2;++i){
if(mx > i){
dis[i] = (dis[id * 2 - i] < (mx - i) ? dis[2 * id - i] : (mx - i));
}
else dis[i] = 1;
while(str2[i - dis[i]] == str2[i + dis[i]]) dis[i]++;
if(i + dis[i] > mx){
mx = i + dis[i];
id = i;
}
}
return len2;
}
後綴數組 √
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) ws[i]=0;
for(i=0;i<n;i++) ws[x[i]=r[i]]++;
for(i=1;i<m;i++) ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,m=p)
{
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) ws[i]=0;
for(i=0;i<n;i++) ws[wv[i]]++;
for(i=1;i<m;i++) ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
int i,j,k=0;
for(i=1;i<=n;i++) rank[sa[i]]=i;
for(i=0;i<n;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
return;
}
迴文樹 √
const int MAXN = 100005;
struct node {
int next[26]; //邊
int len; //迴文串長度
int sufflink; //後綴指針
int num;
};
int len;
char s[MAXN];
node tree[MAXN];
int num; // node 1 - root with len -1, node 2 - root with len 0
int suff; // max suffix palindrome
long long ans;
bool addLetter(int pos) {
int cur = suff, curlen = 0;
int let = s[pos] - 'a';
while (true) {
curlen = tree[cur].len; //當前可能上一個迴文串長度
if (pos - 1 - curlen >= 0 && s[pos - 1 - curlen] == s[pos]) //如果能形成行如XAX的迴文串
break;
cur = tree[cur].sufflink; //查找失敗 找到更小的迴文
}
if (tree[cur].next[let]) { //這個節點已經存在
suff = tree[cur].next[let]; //更新最長前綴值
return false;
}
num++;
suff = num;
tree[num].len = tree[cur].len + 2;
tree[cur].next[let] = num; //加一個節點
if (tree[num].len == 1) { //只有一個X
tree[num].sufflink = 2; //指向空串
tree[num].num = 1;
return true;
}
while (true) {
cur = tree[cur].sufflink;
curlen = tree[cur].len;
if (pos - 1 - curlen >= 0 && s[pos - 1 - curlen] == s[pos]) {
tree[num].sufflink = tree[cur].next[let];
break;
}
}
tree[num].num = 1 + tree[tree[num].sufflink].num;
return true;
}
void initTree() {
num = 2; suff = 2;
tree[1].len = -1; tree[1].sufflink = 1;
tree[2].len = 0; tree[2].sufflink = 1;
}
字典樹
struct Trie{
/**
* maxnode: Trie樹中最多可能的節點個數 上限爲字符串個數 * 最長長度
* sigma_size: 組成字符串的字符種類
* ch: 邊
* value: 節點的值
* sz: Trie樹的總節點個數
*/
int ch[maxnode][sigma_size];
int value[maxnode];
int sz;
int IDX(char c) {return c - 'a';}
Trie(){
sz = 1;
memset(ch[0],0,sizeof(ch[0]));
}
/**
* 字符串s插入Trie中 值爲V
*/
void Insert(char* s,int v){
int u = 0,len = strlen(s);
for(int i = 0;i < len;++i){
int c = IDX(s[i]);
if(!ch[u][c]){
memset(ch[sz],0,sizeof(ch[sz]));
value[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
value[u] = v;
}
/**
* 查找字符串s 返回v
*/
int Search(char* s){
int cur = 0,idx;
while(*s){
idx = IDX(*s);
if(ch[cur][idx] == 0) return -1;
cur = ch[cur][idx];
s++;
}
return value[cur];
}
};
線段樹
#include <bits/stdc++.h>
using namespace std;
/** 線段樹模板 */
/** 以求區間和爲例 */
#define lson rt << 1 , l ,mid
#define rson rt << 1 | 1,mid + 1,r
const int maxn = 50000 + 50;
int v[maxn << 2],laze[maxn << 2];
int A[maxn];
// 向下Push laze標記
void PushDown(int rt,int l,int r){
if(laze[rt]){
int mid = l + r >> 1,lcnt = mid - l + 1,rcnt = r - mid;
v[rt << 1] = v[rt << 1] + lcnt * laze[rt];
v[rt << 1 | 1] = v[rt << 1 | 1] + rcnt * laze[rt];
laze[rt << 1] += laze[rt];laze[rt << 1 | 1] += laze[rt];
laze[rt] = 0;
}
}
//向上更新sum值
void PushUp(int rt){
v[rt] = v[rt << 1] + v[rt << 1 | 1];
}
void Build(int rt,int l,int r){
if(l == r){
v[rt] = A[l];return;
}
int mid = l + r >> 1;
Build(lson),Build(rson);
laze[rt] = 0;
PushUp(rt);
}
//將區間L,R 增加X
void Update(int rt,int l,int r,int L,int R,int x){
if(L <= l && R >= r){
v[rt] += x * (r - l + 1);
laze[rt] += x;
return;
}
PushDown(rt,l,r);
int mid = l + r >> 1;
if(L <= mid) Update(lson,L,R,x);
if(R > mid) Update(rson,L,R,x);
PushUp(rt);
}
int Query(int rt,int l,int r,int L,int R){
if(L <= l && R >= r){
return v[rt];
}
PushDown(rt,l,r);
int mid = l + r >> 1;
int ret = 0;
if(L <= mid) ret += Query(lson,L,R);
if(R > mid) ret += Query(rson,L,R);
PushUp(rt);
return ret;
}
左偏堆
int key[maxn],dis[maxn],ch[maxn][2];
/**合併以a,b爲堆頂的堆*/
int Merge(int a,int b){
if(!a) return b;if(!b) return a;
if(key[a] < key[b]) swap(a,b);
ch[a][1] = Merge(ch[a][1],b);
if(dis[ch[a][0]] < dis[ch[a][1]]) swap( ch[a][0],ch[a][1] );
if(ch[a][1] == 0) dis[a] = 0;
else dis[a] = dis[ch[a][1]] + 1;
return a;
}
大整數 √
struct BigInteger{
int A[25];
enum{MOD = 10000};
BigInteger(){memset(A, 0, sizeof(A)); A[0]=1;}
void set(int x){memset(A, 0, sizeof(A)); A[0]=1; A[1]=x;}
void print(){
printf("%d", A[A[0]]);
for (int i=A[0]-1; i>0; i--){
if (A[i]==0){printf("0000"); continue;}
for (int k=10; k*A[i]<MOD; k*=10) printf("0");
printf("%d", A[i]);
}
printf("\n");
}
int& operator [] (int p) {return A[p];}
const int& operator [] (int p) const {return A[p];}
BigInteger operator + (const BigInteger& B){
BigInteger C;
C[0]=max(A[0], B[0]);
for (int i=1; i<=C[0]; i++)
C[i]+=A[i]+B[i], C[i+1]+=C[i]/MOD, C[i]%=MOD;
if (C[C[0]+1] > 0) C[0]++;
return C;
}
BigInteger operator * (const BigInteger& B){
BigInteger C;
C[0]=A[0]+B[0];
for (int i=1; i<=A[0]; i++)
for (int j=1; j<=B[0]; j++){
C[i+j-1]+=A[i]*B[j], C[i+j]+=C[i+j-1]/MOD, C[i+j-1]%=MOD;
}
if (C[C[0]] == 0) C[0]--;
return C;
}
};
快速輸入 (整數) √
inline bool scan_d(int &num)
{
char in;bool IsN=false;
in=getchar();
if(in==EOF) return false;
while(in!='-'&&(in<'0'||in>'9')) in=getchar();
if(in=='-'){ IsN=true;num=0;}
else num=in-'0';
while(in=getchar(),in>='0'&&in<='9'){
num*=10,num+=in-'0';
}
if(IsN) num=-num;
return true;
}
快速輸入 (實數) √
inline bool scan_lf(double &num)
{
char in;double Dec=0.1;
bool IsN=false,IsD=false;
in=getchar();
if(in==EOF) return false;
while(in!='-'&&in!='.'&&(in<'0'||in>'9'))
in=getchar();
if(in=='-'){IsN=true;num=0;}
else if(in=='.'){IsD=true;num=0;}
else num=in-'0';
if(!IsD){
while(in=getchar(),in>='0'&&in<='9'){
num*=10;num+=in-'0';}
}
if(in!='.'){
if(IsN) num=-num;
return true;
}else{
while(in=getchar(),in>='0'&&in<='9'){
num+=Dec*(in-'0');Dec*=0.1;
}
}
if(IsN) num=-num;
return true;
}
掃描線 √
using namespace std;
const int maxn = 100 + 5;
const int TOT_SEG = maxn * 2;
/** Seg
** l,r,h 分別爲線段的左端點,右端點,高
** f 標識上下邊
*/
struct Seg{
int f;
double l,r,h;
void set(double ll,double rr,double hh,int d){
l = ll,r = rr,h = hh,f = d;
}
}Segs[TOT_SEG];
bool cmp(const Seg& a,const Seg& b){
return a.h < b.h;
}
double AR[TOT_SEG]; /** 區間長度 */
map<double,int> POS; /** 將端點 HASH 到區間上 */
int FALG[TOT_SEG]; /** 區間被覆蓋的次數 */
int Seg_CNT,HASH_SEG_CNT;
void SEG_HASH(){
POS.clear();HASH_SEG_CNT = 2;
sort(Segs + 1,Segs + Seg_CNT,cmp);
sort(AR + 1,AR + Seg_CNT);
for(int i = 2;i < Seg_CNT;++i){
if(AR[i] != AR[i - 1]){
AR[HASH_SEG_CNT] = AR[i];
POS[AR[i]] = HASH_SEG_CNT++;
}
}
for(int i = 1;i < HASH_SEG_CNT;++i){
AR[i] = AR[i + 1] - AR[i];
}
}
1~MOD的逆元 √
#define mod 9973ll
typedef long long LL;
LL inv[mod];
inv[1] = 1;
for(int i = 2;i < mod;++i) inv[i] = (mod - mod / i) * (inv[mod % i]) % mod;