并查集
int find(int x)
{
if(father[x]!=x)
father[x]=find(father[x]);
return father[x];
}
void Merge(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
father[x]=y;
}
ST 表
以求最大值为例
先是两个预处理:
void init_log()
{
Log[1]=0;
for(int i=2;i<=n;++i)
Log[i]=Log[i/2]+1;
}
void init_ST()
{
int i,j;
for(i=1;i<=n;++i)
f[i][0]=a[i];
for(j=1;(1<<j)<=n;++j)
for(i=1;i+(1<<j-1)<=n;++i)
f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
查询 [    l , r    ] [ \;l , r\; ] [l,r] 内的最大值:
int solve(int l,int r)
{
int k=Log[r-l+1];
return max(f[l][k],f[r-(1<<k)+1][k]);
}
LCA
用倍增求, f a i , j fa_{i,j} fai,j 表示从 i i i 往上跳 2 j 2^j 2j 步的祖先
void init()
{
int i,j;
for(j=1;j<=18;++j)
for(i=1;i<=n;++i)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
int Lca(int x,int y)
{
int i;
if(dep[x]<dep[y]) swap(x,y);
for(i=18;~i;--i)
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if(x==y) return x;
for(i=18;~i;--i)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
树的直径
两次 d f s dfs dfs
void dfs(int x,int father)
{
int i,j;
if(ans<d[x])
ans=d[x],p=x;
for(i=first[x];i;i=nxt[i])
{
j=v[i];
if(j!=father)
{
d[j]=d[x]+w[i];
dfs(j,x);
}
}
}
void solve()
{
ans=0,d[1]=0,dfs(1,0);
ans=0,d[p]=0,dfs(p,0);
printf("%d",ans);
}
树形 d p dp dp
void dp(int x,int father)
{
int i,j;
for(i=first[x];i;i=nxt[i])
{
j=v[i];
if(j!=father)
{
dp(j,x);
if(f1[x]<f1[j]+w[i])
{
f2[x]=f1[x];
f1[x]=f1[j]+w[i];
}
else if(f2[x]<f1[j]+w[i])
f2[x]=f1[j]+w[i];
}
}
}
void solve()
{
int i,ans=0;
for(i=1;i<=n;++i)
ans=max(ans,f1[i]+f2[i]);
printf("%d",ans);
}
树的重心
void dfs(int x,int father)
{
int i,j;
Max[x]=0,size[x]=1;
for(i=first[x];i;i=nxt[i])
{
j=v[i];
if(j!=father)
{
dfs(j,x);
size[x]+=size[j];
Max[x]=max(Max[x],size[j]);
}
}
Max[x]=max(Max[x],n-size[x]);
if(num>Max[x]) pos=x,num=Max[x];
}
树状数组
以单点修改,区间查询为例吧
#define lowbit(x) x&-x
void add(int i,int x)
{
while(i<=n)
{
bit[i]+=x;
i+=lowbit(i);
}
}
int sum(int i)
{
int ans=0;
while(i)
{
ans+=bit[i];
i-=lowbit(i);
}
return ans;
}
线段树
建树
void Build(int root,int l,int r)
{
if(l==r)
{
sum[root]=a[l];
return;
}
int mid=(l+r)>>1;
Build(root<<1,l,mid);
Build(root<<1|1,mid+1,r);
sum[root]=sum[root<<1]+sum[root<<1|1];
}
区间加
void Add(int root,int l,int r,int x,int y,int k)
{
if(l>=x&&r<=y)
{
add[root]+=k;
sum[root]+=1ll*(r-l+1)*k;
return;
}
int mid=(l+r)>>1;
Pushdown(root,l,r,mid);
if(x<=mid) Add(root<<1,l,mid,x,y,k);
if(y>mid) Add(root<<1|1,mid+1,r,x,y,k);
sum[root]=sum[root<<1]+sum[root<<1|1];
}
标记下传
void Pushdown(int root,int l,int r,int mid)
{
add[root<<1]+=add[root];
add[root<<1|1]+=add[root];
sum[root<<1]+=1ll*(mid-l+1)*add[root];
sum[root<<1|1]+=1ll*(r-mid)*add[root];
add[root]=0;
}
区间求和
long long Query(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return sum[root];
long long ans=0;
int mid=(l+r)>>1;
Pushdown(root,l,r,mid);
if(x<=mid) ans+=Query(root<<1,l,mid,x,y);
if(y>mid) ans+=Query(root<<1|1,mid+1,r,x,y);
return ans;
}
Ps:注意线段树要开四倍空间啊