2018年模板大集合!!!!没有一个优秀的模板就是等着被摩擦

版权声明:本文为博主原创文章,如果转走了就评论说一声就好了哈。 https://blog.csdn.net/qq_36124802/article/details/80473338

离散化模板:

//把需要离散化的内容进行排序,去重,然后getid为取得这个值的下标
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int getid(int x){
	return lower_bound(v.begin(),v.end(),x)-v.begin()+1; //错1次,返回下标需要-v.begin()+1 
}

链式前向星:

struct E{
    int next,to;
}e[maxn+maxn];  //链式前向星要开双倍的大小
void add(int u,int v){
    tot++;
    e[tot].to=v;
    e[tot].next=head[u];
    head[u]=tot;
}

三种树状数组(每次操作复杂度均为logn):

1、区间求值,单点修改(基本用法。但是这里可用用作权值树状数组,可以用来查比a[i]大但是下标在i前面的数量

ll lowbits(ll x){
    return x&(-x);
}
ll NN;
ll c1[maxn];//注意这里空间可能要开大一些
ll sum(ll x){
    ll sum1=0;
    while(x>0){
    sum1+=c1[x];
    x-=lowbits(x);
    }
    return sum1;
}
ll add(ll x,ll y){
    while(x<=NN){
        c1[x]+=y;
        x+=lowbits(x);
    }
}

2、单点求值,区间修改(区间加

ll l=3;
    ll r=7,k=10;
    add(l,k);
    add(r+k,-k);//表示对区间[l,r]+k,查询某个点只需要查询sum(i)即为答案

3、区间求值,区间修改(区间加

int NN,c1[maxn],c2[maxn];
int lowbits(int x){ return x&(-x);}
void add(int *r,int pos,int v){
	while(pos<=NN){
		r[pos]+=v;
		pos+=lowbits(pos);
	}
}
int sigma(int *r,int pos){
	int anx=0;
	while(pos>0){
		anx+=r[pos];
		pos-=lowbits(pos);
	}
	return anx;
}

c1与c2均为数组传入,对[f1,f2]+f3

			add(c1,f1,f3);add(c1,f2+1,-f3);
            add(c2,f1,f3*(f1-1));add(c2,f2+1,-f3*f2);

查询[f1,f2]

sum1=(f1-1)*sigma(c1,f1-1)-sigma(c2,f1-1);
            sum2=f2*sigma(c1,f2)-sigma(c2,f2);
            cout << sum2-sum1 << endl;

并查集:

先init初始化,find1为寻找其的head,find2为并

int find1(int x){
	int r=x;
	while(r!=pre[r]){
		r=pre[r];
	}
	int i=x,j;
	while(pre[i]!=r){
		j=pre[i];
		pre[i]=r;
		i=j;
	}
	return r;
}
int find2(int x,int y){
    int x1=find1(x);
    int y1=find1(y);
    if(pre[x1]!=pre[y1]){
        pre[x1]=pre[y1];
    }
}

二分图匹配:

记得在for循环中,赋值vis全部为0,从左边点找右边点,但是算谁连接谁点时候应该从右边点点向前找pre

int bfs(int x){//最后pre[i]=x,表示i被X连接
    for(int i=0;i<out[x].size();i++){
        int v=out[x][i];
        if(vis[v]==0){
            vis[v]=1;
        if(pre[v]==-1||bfs(pre[v])){
            pre[v]=x; //X之前连接点为out[x][i]
            return 1;//这里点目的是求出最大匹配的数量
        }
    }
    }
    return 0;
}

通过逆元求组合数:

int inv(int a)
{
	//return fpow(a, MOD-2, MOD);
	return a==1?1:(long long)(MOD-MOD/a)*inv(MOD%a)%MOD;
}

LL C(LL n,LL m)
{
	if(m<0)return 0;
	if(n<m)return 0;
	if(m>n-m)m=n-m;
	LL up=1,down=1;
	for(LL i=0;i<m;i++)
	{
		up=up*(n-i)%MOD;
		down=down*(i+1)%MOD;
	}
	return up*inv(down)%MOD;
}

二进制压缩的背包DP:

        for(i=1;i<=n;i++){
            for(j=0;j<q1[i].num;j++){ //因为是2^k-1个,所以取的时候是1/2/4/8/2^(k-1)刚好全部覆盖
                for(k=10000;k>=q1[i].v;k--){
                    if(k-(1<<j)*q1[i].v<0)break;
                    res[k]+=res[k-(1<<j)*q1[i].v];
                    res[k]%=mod;
                }
            }
        }

矩阵快速幂板子:

const int N = 9;
struct Mat{
	ll m[N][N];
	void clear(){memset(m,0,sizeof(m));}
	void I(){for (int i=0;i<N;i++) m[i][i]=1;}
};
Mat mul(const Mat &a, const Mat &b){
	Mat c;c.clear();
	for (int i=0;i<N;i++)
		for (int j=0;j<N;j++){
			for (int k=0;k<N;k++)
				c.m[i][j]+=a.m[i][k]*b.m[k][j]%mod;
			c.m[i][j]%=mod;
		}
	return c;
}
Mat qpow(Mat a, ll x){ //这里注意传进来的参数要为long long 不然会报超时
	Mat ans; ans.clear(); ans.I();
	for (;x;a=mul(a,a),x>>=1)
		if (x&1)
			ans=mul(ans,a);
	return ans;
}
Mat t2=(Mat){{
{0,0},
{1,0}
}}; //初始化一个矩阵可以这么写出来

读入挂:

inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}

树剖的方法求LCA:

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+7;
struct node{
    int to,next;
};
node edge[maxn+maxn]; //链式前向星开双倍
int cnt,head[maxn],pos;//注意初始化cnt和head为-1
void add(int x,int y){
    cnt++;
    edge[cnt].to=y;
    edge[cnt].next=head[x];
    head[x]=cnt;
}
int son[maxn],deep[maxn],fa[maxn],num[maxn];
void init(){
    pos=0;
    memset(son,-1,sizeof(son));//点的编号从1开始
}
void dfs(int u,int pre,int w){
    deep[u]=w;//层数
    num[u]=1;//子节点数量
    fa[u]=pre;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre)continue;
        dfs(v,u,w+1);
        num[u]+=num[v];
        if(son[u]==-1||num[son[u]]<num[v])
        son[u]=v;
    }
}
int p[maxn],fp[maxn],tp[maxn];
//p表示这个点的位置在哪(线段树中的下标)
//fp表示这个位置指哪一个点建树的时候就这么建
//tp表示v在哪个重链上
void dfs2(int u,int sd){
    p[u]=++pos;fp[pos]=u;tp[u]=sd;
    if(son[u]==-1)return ;
    dfs2(son[u],sd);
    for(int i=head[u];i!=-1;i=edge[i].next){ //注意head[u]
        int v=edge[i].to;
        if(v==son[u]||v==fa[u])continue;
        dfs2(v,v); //此时v为新链
    }
}
int LCA(int u,int v){
    int uu=tp[u],vv=tp[v];
    while(uu!=vv){
        if(deep[vv]>deep[uu]){
            swap(uu,vv);swap(u,v);
        }
        //从左边往右边应该是p[uu],p[u]
        u=fa[u];uu=tp[u];
    }
    if(deep[u]>deep[v])swap(u,v);
    //p[u],p[v] //u为根节点
    return u;
}
int main(){
    int i,j,k,f1,f2,f3,f4,f5,t1,t2,t3,t4,t5;
    //freopen("in.txt","r",stdin);
    int n,m,Q,S;
    scanf("%d %d %d",&n,&Q,&S);
    cnt=0;
    memset(head,-1,sizeof(head));
    init();
    for(i=1;i<n;i++){
        scanf("%d %d",&t1,&t2);
        add(t1,t2);
        add(t2,t1);
    }
    dfs(S,-1,1);
    dfs2(S,S);
    for(i=1;i<=Q;i++){
        scanf("%d %d",&t1,&t2);
        printf("%d\n",LCA(t1,t2));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36124802/article/details/80473338