ACM板子¶
重链剖分(HLD)¶
namespace HLD{
//树的常规信息---
vector<int>tree[N];
int fa[N],size[N],depth[N];//深度用于lca
//---树的常规信息
int top[N];//链头
int wson[N];//重儿子
int hld_dfn[N],dfncnt;//dfn序(一条重链和一个子树dfn序连续
void dfs1(int u,int father){//第一次dfs找到重儿子,父亲,子树重量和深度
size[u]=1;fa[u]=father;
depth[u]=depth[father]+1;
for(int v:tree[u])
{
if(v!=father){//不回父边
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[wson[u]]) wson[u]=v;//重儿子更换
}
}
}
void dfs2(int u,int father,int utop){//第二次dfs创建重链,标记链头,找到dfn序
top[u]=utop;
hld_dfn[u]=(++dfncnt);//dfs序
//.........//可添加dfs序维护的东西
if(wson[u]!=0) dfs2(wson[u],u,utop);//先遍历重儿子,链头不变
for(int v:tree[u]){
if(v!=father&&v!=wson[u]){
dfs2(v,u,v);//再遍历轻儿子,链头变为轻儿子自身
}
}
}
int lca(int u,int v){//使用HLD找到LCA//也可改为返回ans
while(top[u]!=top[v])
{
if(depth[top[u]]>depth[top[v]]) swap(u,v);//保证v的链头深度较深
//...//这里可以更新dfs序中的[hld_dfn[top[v]],hld_dfn[v]]
v=fa[top[v]];
}
if(depth[u]>depth[v]) swap(u,v);// 保证v比较深
//...//这里可以更新[hld_dfn[u],hld_dfn[v]]
return u;
}
}
树状数组¶
namespace BIT{
int bit[N];//树状数组本体
int n;//树状数组大小
int lowbit(int x) {return x&(-x);}
void update(int id,int date)//单点加
{
for(int i=id;i<=n;i+=lowbit(i)) bit[i]+=date;
}
int query(int id)//查询前缀和
{
int res=0;
for(int i=id;i>0;i-=lowbit(i)) res+=bit[i];
return res;
}
}
快速幂¶
递归写法¶
long long fast_pow(long long a,long long k){
if(k==0) return 1;
long long res=fast_pow(a,k>>1)%p;
if(k%2)
return ((res*res)%p*a)%p;
else
return (res*res)%p;
}
非递归写法¶
long long fast_pow(long long a,long long k)
{
//a为当前位对应的a的幂次
long long res=1;
while(k>0)
{
if(k&1) res=(res*a)%p;
a=(a*a)%p;
k>>=1;
}
return res;
}
memset¶
无论t是几维数组,均可以使用
memset(t,val,sizeof(t));
进行初始化,使其全部为val,例如当val=0时数组将被全部初始化为0
Note
注意:int数组只能初始化为-1或0,char数组则可以任意(有关字节存储问题)
SAM¶
注:并未完全完善
namespace SAM{
void text();
int last=0;//原串所在节点
int cnt=0;//目前开创节点
struct node
{
int son[26];
int father=-1;//link树上的
int num;
int len;
}sam[N<<1];
void newnode(int length,int number){//建立新节点满足
//cnt++;
sam[++cnt].len=length;
sam[cnt].father=-1;//先设父节点未知(建完后只有根节点0父节点未知)
sam[cnt].num=number;
memset(sam[cnt].son,0,sizeof(sam[cnt].son));
}
void insert(int c){//输入单字符(按字母表转化为数字)
newnode(sam[last].len+1,1);//建立新endpos
int now=cnt,p=last;//现在的节点编号和正在跳father的节点编号
//cout<<now<<" "<<p<<endl;
while(!sam[p].son[c]&&p!=-1)//跳father直到有这个儿子或者到头(越过0节点的头)
{
//cout<<p<<endl;
sam[p].son[c]=now;
p=sam[p].father;
}
if(p==-1)
sam[now].father=0;//cout<<"ee"<<endl;//父亲为根
else{
int sonnode=sam[p].son[c];//待拆点/处理的点
if(sam[sonnode].len==sam[p].len+1) sam[now].father=sonnode;//不用拆点
else{//拆点
//cout<<"ee";
newnode(sam[p].len+1,0);
int newson=cnt;
sam[newson].father=sam[sonnode].father;
sam[sonnode].father=newson;//父亲关系
memcpy(sam[newson].son,sam[sonnode].son,sizeof(sam[sonnode].son));//儿子关系
//前面的儿子关系
while(p!=-1&&sam[p].son[c]==sonnode)
{
sam[p].son[c]=newson;//变更关系
p=sam[p].father;
}
sonnode=newson;//便于后边统计数量
sam[now].father=newson;
}
//cout<<sonnode<<endl;
}
last=now;
}
int query(string s)//查询字符串s次数
{
int now=0,flag=1;
for(int i=0;i<s.length();i++){
int id=s[i]-'a';
if(!sam[now].son[id]) {flag=0;break;}
now=sam[now].son[id];
}
if(flag) return sam[now].num;
else return 0;
}
void add(string t){//往后插入t
for(char i:t)
{
insert(i-'a');
//cout<<sam[cnt].len-sam<<endl;
//text();
}
}
long long total()//找子串个数
{
long long res=0;
for(int i=1;i<=cnt;i++)
{ if(sam[i].num!=1)
res=max(res,1ll*sam[i].num*sam[i].len);
}return res;
}
void text(){//输出SAM
cout<<"-----------------------------------------------"<<endl;
for(int i=1;i<=cnt;i++)
{
cout<<i<<":father-"<<sam[i].father<<endl;
cout<<"len:"<<sam[i].len<<" num:"<<sam[i].num<<endl;
cout<<"son:";
for(int j=0;j<=25;j++){
if(sam[i].son[j]) cout<<j<<"-"<<sam[i].son[j]<<" ";
}
cout<<endl<<endl;
}
cout<<"-----------------------------------------------"<<endl;
}
}