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


    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].next

    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的时候别写反了……
    下饭集锦:
    跑最短路,我写的大根堆……

    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吧

    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维护的是当前次大值,更新完后如果变成最大值了记得交换一下
    下饭集锦:
    没有下饭qwq

    二分图

    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数组
    下饭集锦:

    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还有什么值得注意的么……
    下饭集锦:

    数论

    线性筛

    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啊!!!qwq

    乘法逆元

    如果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
    ",(x%mod+mod)%mod);
    }
    

    注意事项:
    线性递推的时候(mod-dfrac{mod}{i})千万不要提mod变成(mod*dfrac{i-1}{i}),不然永远是0(笑);
    exgcd记不住就重新推一遍,(x->y , y->x-lfloor{dfrac{a}{b}} floor *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
    ",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]]++;
    }
    

    注意事项:
    如果初始分块没有覆盖所有点,把最后几个点也放入块中
    下饭集锦:

  • 相关阅读:
    BZOJ2140: 稳定婚姻(tarjan解决稳定婚姻问题)
    BZOJ2124: 等差子序列(树状数组&hash -> bitset 求是否存在长度为3的等差数列)
    HDU 1217 Arbitrage(Bellman-Ford判断负环+Floyd)
    HDU 2112 Today(Dijkstra+map)
    HDU 2066 一个人的旅行(dijkstra水题+判重边)
    POJ 1511 Invitation Cards(Dijkstra(优先队列)+SPFA(邻接表优化))
    HDU 2544 最短路(floyd+bellman-ford+spfa+dijkstra队列优化)
    POJ 2431 Expedition (贪心 + 优先队列)
    POJ 3253 Fence Repair(哈夫曼编码)
    优先队列的使用(转)
  • 原文地址:https://www.cnblogs.com/tqr06/p/11846672.html
Copyright © 2020-2023  润新知