CSP前有用的板子(更新至十一篇)

[TOC]數組

CSP前把重要的板子所有打一遍吧,順便放一些下飯集錦(在每一個板子的下面)數據結構

圖論

SPFA

ll dis[N];
bool vis[N];
queue<int> q;
void spfa(int s)
{
	memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
	q.push(s);vis[s]=1;dis[s]=0;
	while(!q.empty())
	{
		int u=q.front();q.pop();vis[u]=0;
		for(register int i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].dis)
			{
				dis[v]=dis[u]+edge[i].dis;
				if(!vis[v])
				{
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}

注意事項: 沒什麼好注意的,spfa極其優美,手感溫馨; 下飯集錦: 腦子一抽把v搞成了edge[i].nextspa

Dijkstra

struct Node
{
	int u;
	ll dis;
	bool operator < (const Node &a) const
	{
		return a.dis<dis;
	}
};

ll dis[N];
bool vis[N];
priority_queue<Node> q;
void dijkstra(int s)
{
	memset(vis,0,sizeof(vis));
	memset(dis,0x7f,sizeof(dis));
	q.push(Node{s,0});dis[s]=0;
	while(!q.empty())
	{
		int u=q.top().u;q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(register int i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].dis)
			{
				dis[v]=dis[u]+edge[i].dis;
				q.push(Node{v,dis[v]});
			}
		}
	}
}

注意事項: 第一個節點不要更新vis標記,在循環的時候再判斷和更新; 取出優先隊列元素的時候也不用清空標記; 自定義Node的時候別寫反了…… 下飯集錦: 跑最短路,我寫的大根堆……code

LCA

int anc[N][25];
int fa[N],dep[N];
void dfs(int u)
{
	anc[u][0]=fa[u];
	for(register int i=1;i<=20;++i) anc[u][i]=anc[anc[u][i-1]][i-1];
	for(register int i=head[u];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(v==fa[u]) continue;
		fa[v]=u;
		dep[v]=dep[u]+1;
		dfs(v);
	}
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	int delta=dep[x]-dep[y];
	for(register int i=0;delta;delta>>=1,++i)
		if(delta&1) x=anc[x][i];
	if(x==y) return x;
	for(register int i=20;anc[x][0]!=anc[y][0];--i)
	{
		if(anc[x][i]!=anc[y][i])
		{
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	return anc[x][0];
}

注意事項: 預處理後,先保證深度x>=y,而後delta倍增往下跳,注意第一重循環從1開始! 下飯集錦: for(register int i=0;delta;delta>>=1,++i)寫成了for(register int i=0;delta>>=1;++i) 結果就是進入循環的時候就已經除以2了qwq隊列

樹的直徑

樹的直徑能夠兩次dfs很簡單地求出來,這兒放一個樹形dp的方法,實在沒記住就dfs吧ci

void dfs(int u)
{
    sum1=sum2=0;
    for(register int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[u]) continue;
        fa[v]=u;
        sum2=max(sum2,dfs(v)+edge[i].dis);
        if(sum2>sum1) swap(sum2,sum1);
    }
    ans=max(ans,sum1+sum2);
    return ans;
}

注意事項: sum2維護的是當前次大值,更新完後若是變成最大值了記得交換一下 下飯集錦: 沒有下飯qwqit

二分圖

int match[N];
bool used[N];
bool dfs(int pos)
{
	for(register int i=1;i<=m;++i)
	{
		if(vis[pos][i]&&!used[i])//這條邊存在且沒用過 
		{
			used[i]=1;
			if(!match[i]||dfs(match[i]))//若是還沒匹配或能夠從新匹配
			{
				match[i]=pos;
				return true;
			}
		}
	}
	return false;
}

注意事項: 對左邊的每一個點進行搜索前,要清空used數組 下飯集錦: 無io

Kruskal

void kruskal()
{
	sort(edge+1,edge+m+1,cmp);
	for(register int i=1;i<=n;++i) fa[i]=i;
	for(register int i=1;i<=m;++i)
	{
		int fx=find(edge[i].from);
		int fy=find(edge[i].to);
		if(fx==fy) continue;
		fa[fx]=fy;
		ans+=edge[i].dis;
		if(++num==n-1) break;
	}
}

注意事項: kruskal還有什麼值得注意的麼…… 下飯集錦: 無class

數論

線性篩

int prime[N],num;
bool notprime[N];
void Ls(int maxn)
{
	for(register int i=2;i<=maxn;++i)
	{
		if(!notprime[i]) prime[++num]=i;
		for(register int j=1;j<=num&&prime[j]*i<=maxn;++j)
		{
			notprime[prime[j]*i]=1;
			if(!(i%prime[j])) break;
		}
	}
}

注意事項: j從1開始枚舉,乘的時候注意邊界 下飯集錦: 沒有特判1!注意特判notprime[1]=1啊!!!qwqsed

乘法逆元

若是mod是質數,能夠直接費馬小定理求逆元

ll qpow(int n,int k)
{
	ll res=1;
	while(k)
	{
		if(k&1) res=(res*n)%mod;
		n=(n*n)%mod;
		k>>=1;
	}
	return res;
}

ll inv(int n)
{
	return qpow(n,mod-2);
}

當要求不少連續的逆元時,能夠線性遞推求解(二式是階乘逆元) $$inv[i]=inv[mod%i]×(mod-\frac{mod}{i})$$

$$facinv[i]=facinv[i+1]*(i+1)$$

void init()
{
    inv[1]=1;
    for(register int i=2;i<=n;++i)
        inv[i]=inv[mod%i]*(mod-mod/i)%mod;
}

若是不保證mod是質數,但n和mod互質,就能夠exgcd求解了

ll x,y;
void exgcd(int a,int b)
{
	if(!b) {x=1,y=0;return;}
	exgcd(b,a%b);
	int z=x;x=y;y=z-a/b*y;
}

int main()
{
    read(n);read(mod);
    exgcd(n,mod);
    printf("%lld\n",(x%mod+mod)%mod);
}

注意事項: 線性遞推的時候$mod-\dfrac{mod}{i}$千萬不要提mod變成$mod*\dfrac{i-1}{i}$,否則永遠是0(笑); exgcd記不住就從新推一遍,$x->y\ ,\ y->x-\lfloor{\dfrac{a}{b}}\rfloor *y$ 下飯集錦: 我還真把mod給提公約數了,而後逆元全是0……

其餘

ST表

int main()
{
	read(n);read(m);
	for(register int i=1;i<=n;++i) read(Max[i][0]);
	Lg[0]=-1;
	for(register int i=2;i<=100002;++i) Lg[i]=Lg[i/2]+1;
	for(register int j=1;j<=25;++j)
		for(register int i=1;i+(1<<j)-1<=n;++i)
			Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);
	for(register int i=1;i<=m;++i)
	{
		read(l);read(r);
		int LG=Lg[r-l+1];
		printf("%d\n",max(Max[l][LG],Max[r-(1<<LG)+1][LG]));
	}
	return 0;
}

注意事項: 沒啥好注意的,最多就是判邊界的時候當心點吧 下飯集錦:

數據結構

樹狀數組

void add(int pos,int v)
{
	while(pos<=n)
	{
		b[pos]+=v;
		pos+=lowbit(pos);
	}
}

ll query(int pos)
{
	ll res=0;
	while(pos)
	{
		res+=b[pos];
		pos-=lowbit(pos);
	}
	return res;
}

注意事項: 加的時候逐步加lowbit,詢問的時候逐步減lowbit(廢話) 下飯集錦: 若是樹狀數組都下飯我就沒了啊……

分塊

int query(int l,int r,int x)
{
    int p=pos[l],q=pos[r],res=0;
    if(p==q)
    {
    	for(register int i=l;i<=r;++i) if(a[i]==x) res++;
        return res;
    }
    for(register int i=p+1;i<=q-1;++i) res+=b[i][x];
    for(register int i=l;i<=R[p];++i) if(a[i]==x) res++;
    for(register int i=L[q];i<=r;++i) if(a[i]==x) res++;
    return res;
}

int main()
{
    num=sqrt(n);
    for(register int i=1;i<=num;++i)
    {
        L[i]=(i-1)*num+1;
        R[i]=i*num;
    }
    if(R[num]<n) num++,L[num]=R[num-1]+1,R[num]=n;
    for(register int i=1;i<=num;++i)
        for(register int j=L[i];j<=R[i];++j)
            pos[j]=i,b[i][a[j]]++;
}

注意事項: 若是初始分塊沒有覆蓋全部點,把最後幾個點也放入塊中 下飯集錦: 無

相關文章
相關標籤/搜索