模板收集

由于找到的模板大都不全,于是决定自己写

优化类

快读

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}

快写

inline void write(int x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}

算法

gcd

inline int gcd(int x, int y){return y?gcd(y,x%y):x;}
inline int lcm(int GCD,int n,int m){return n*m/GCD;}

快速幂

ll qpow(ll a,ll n)
{
	ll ans=1;
	while(n)
	{
		if(n&1) ans*=a,ans%=mod;
		a*=a;
		a%=mod;
		n>>=1;
	}
	return ans%mod;
}

UFDS

int fa[5005],n;
inline int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
inline void com(int x,int y)
{
	fa[find(x)]=find(y);
}
inline void init()
{
	for(register int i=1;i<=2*n;i++) fa[i]=i;
}

最小生成树

bool cmp(edg a,edg b)
{
	return a.w>b.w;
}
/*
需加入并查集
e1储存的是原图 e2是最小生成树
*/
void Kru()
{
	sort(e1+1,e1+m+1,cmp);
	init();
	for(int i=1;i<=m;++i)
	{
		if(find(e1[i].u)!=find(e1[i].v))
		{
			com(e1[i].u,e1[i].v);
			add(e1[i].u,e1[i].v,e1[i].w);
			add(e1[i].v,e1[i].u,e1[i].w);
		}
	}
}

LCA

int head[maxn],d[maxn],p[maxn][21];//d存深度(deep)p存祖先
struct node{
    int v,next;
}e[maxn*2];
void add(int u,int v)
{
    e[k].v=v;
    e[k].next=head[u];
    head[u]=k++;
}
void dfs(int u,int fa)//dfs(root,0);
{
    d[u]=d[fa]+1;
    p[u][0]=fa;
    for(int i=1;(1<<i)<=d[u];i++) p[u][i]=p[p[u][i-1]][i-1];
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(v!=fa) dfs(v,u);
    }
}
int lca(int a,int b)
{
    if(d[a]>d[b]) swap(a,b);
    for(int i=20;i>=0;i--) if(d[a]<=d[b]-(1<<i)) b=p[b][i];
    if(a==b) return a;
    for(int i=20;i>=0;i--)
    {
        if(p[a][i]==p[b][i]) continue;
        else a=p[a][i],b=p[b][i];
    }
    return p[a][0];
}

最短路

Floyd

int dis[N][N];
memset();
for(int k=1;k<=n;++k)
	for(int i=1;i<=n;++i)
    	for(int j=1;j<=n;++j)
        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);

Dij

int dis[N],vis[N];
void dij(int s)
{
	typedef pair<int,int> pii;
    priority_queue<pii,vector<pii>,greater<pii> > q;
    dis[s]=0;
    q.push(make_pair(dis[s],s);
    while(!q.empty())
    {
    	int u=q.top();
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=e[i].next)
        {
        	int v=e[i].v;
            if(dis[v]>dis[u]+e[i].w)
            {
            	dis[v]=dis[u]+e[i].w;
                q.push(make_pair(dis[v],v));
            }
        }
    }
}

SPFA

inline bool spfa(int s)
{
    memset(dis,0x7f7f7f7f,sizeof dis);
    memset(vis,0,sizeof vis);
    memset(t,0,sizeof t);
    queue<int> q;
    q.push(s);
    dis[s]=0;
    vis[s]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i;i=e[i].next)
        {
            int v=e[i].v;
            if(dis[v]>dis[now]+e[i].w)
            {
                dis[v]=dis[now]+e[i].w;
                if(!vis[v])
                {
                    vis[v]=1;
                    ++t[v];
                    if(t[v]>=n) return false;//判断负环
                    q.push(v);
                }
            }
        }
    }
    return true;
}

DS

单调队列

就拿样例来谈谈,设以最小的为标准。

8 3
1 3 -1 -3 5 3 6 7

下文中我们用q来表示单调队列,p来表示其所对应的在原列表里的序号。

由于此时队中没有一个元素,我们直接令1进队。此时,q={1},p={1}。

现在3面临着抉择。下面基于这样一个思想:假如把3放进去,如果后面2个数都比它大,那么3在其有生之年就有可能成为最小的。此时,q={1,3},p={1,2}

下面出现了-1。队尾元素3比-1大,那么意味着只要-1进队,那么3在其有生之年必定成为不了最小值,原因很明显:因为当下面3被框起来,那么-1也一定被框起来,所以3永远不能当最小值。所以,3从队尾出队。同理,1从队尾出队。最后-1进队,此时q={-1},p={3}

出现-3,同上面分析,-1>-3,-1从队尾出队,-3从队尾进队。q={-3},p={4}。

出现5,因为5>-3,同第二条分析,5在有生之年还是有希望的,所以5进队。此时,q={-3,5},p={4,5}

出现3。3先与队尾的5比较,3<5,按照第3条的分析,5从队尾出队。3再与-3比较,同第二条分析,3进队。此时,q={-3,3},p={4,6}

出现6。6与3比较,因为3<6,所以3不必出队。由于3以前元素都<3,所以不必再比较,6进队。因为-3此时已经在滑动窗口之外,所以-3从队首出队。此时,q={3,6},p={6,7}

出现7。队尾元素6小于7,7进队。此时,q={3,6,7},p={6,7,8}。

那么,我们对单调队列的基本操作已经分析完毕。因为单调队列中元素大小单调递*(增/减/自定义比较),因此,队首元素必定是最值。按题意输出即可。

int m;//窗口大小
inline void win1(int m)//min
{
	int h1=1,t1=0;
	int sum=0;
    for(register int i=1;i<=n;++i)
    {	
		while(h1<=t1&&q1[h1]+m<=i) h1++;
        while(h1<=t1&&a[i]<a[q1[t1]]) t1--;
        q1[++t1]=i;
        if(i>=m) printf("%d ",a[q1[h1]]);
    }
    puts("");
}
inline void win2(int m)//max
{
	int h2=1,t2=0;
    for(register int i=1;i<=n;++i)
    {
        while(h2<=t2&&q2[h2]+m<=i) h2++;
        while(h2<=t2&&a[i]>a[q2[t2]]) t2--;
        q2[++t2]=i;
        if(i>=m) printf("%d ",a[q2[h2]]);
    }
    puts("");
}

BIT

int lowbit(int x){
    return x&-x;
}void add(int x,int k){//单点修改
    while(x<=n){//当在最大区间范围内时
        c[x]=c[x]+k;//对当前节点进行修改
        x=x+lowbit(x);//“跳跃”到下一个节点
    }
}int getsum(int x){//求a1,a2,a3……ax的和
    int ans=0;
    while(x>=1){//当x在给定范围内
        ans+=c[x];//求和
        x-=lowbit(x);//跳跃到下一个节点
    }return ans;
}inline void init(){//O(n)建树
    for(int i=1;i<=n;++i){
        t[i]+=a[i];
        int j=i+lowbit(i);
        if(j<=n)t[j]+=t[i];
    }
}

ST

ST表

ST表的功能很简单

它是解决RMQ问题(区间最值问题)的一种强有力的工具

它可以做到\(O(nlogn)\)预处理,\(O(1)\)查询最值

算法

ST表是利用的是倍增的思想

拿最大值来说

我们用\(Max[i][j]\)表示,从ii位置开始的\(2^j\)个数中的最大值,例如\(Max[i][1]\)表示的是ii位置和\(i+1\)位置中两个数的最大值

那么转移的时候我们可以把当前区间拆成两个区间并分别取最大值(注意这里的编号是从\(1\)开始的)

查询的时候也比较简单

我们计算出\(log_2{\text{区间长度}}\)

然后对于左端点和右端点分别进行查询,这样可以保证一定可以覆盖查询的区间

刚开始学的时候我不太理解为什么从右端点开始查的时候左端点是\(r-2^k+1\)

实际很简单,因为我们需要找到一个点\(x\),使得\(x+2^k-1=r\)

这样的话就可以得到\(x=r-2^k+1\)

上面讲的可能比较抽象,建议大家画个图好好理解一下

int Max[MAXN][21];
int Query(int l,int r)
{
    int k=log2(r-l+1); 
    return max(Max[l][k],Max[r-(1<<k)+1][k]);//把拆出来的区间分别取最值 
}
//int main()
    int N=read(),M=read();//M->question
    for(int i=1;i<=N;i++) Max[i][0]=read();
    for(int j=1;j<=21;j++)
        for(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(int i=1;i<=M;i++)
    {
        int l=read(),r=read();
        printf("%d\n",Query(l,r));
    }

Segment Tree

int n,a[100005],d[300005],b[300005];//b->lazytag
void build(int l,int r,int p)
{
	if(l==r)
	{
		d[p]=a[l];
		return;
	}
	int m=(l+r)>>1;
	build(l,m,p<<1),build(m+1,r,(p<<1)|1);
	d[p]=d[p<<1]+d[(p<<1)|1];
}
int getsum(int l,int r,int s,int t,int p)
{
	if (l<=s&&t<=r) return d[p];
  	int m=(s + t)>>1;
  	if(b[p])
    	d[p<<1]+=b[p]*(m-s+1),d[(p<<1)|1]+=b[p]*(t-m),
        b[p<<1]+=b[p],b[(p<<1)|1]+=b[p];
  	b[p]=0;
  	int sum=0;
  	if(l<=m) sum+=getsum(l,r,s,m,p<<1);
  	if(r>m) sum+=getsum(l,r,m+1,t,(p<<1)|1);
  	return sum;
}
void update(int l,int r,int c,int s,int t,int p)
{
	// [l,r] 为修改区间,c 为被修改的元素的变化量,[s,t] 为当前节点包含的区间,p当前节点的编号
	if(l<=s&&t<=r)
	{
		d[p]+=(t-s+1)*c,b[p]+=c;
		return ;
	}
	int m=(s+t)>>1;
	if(b[p])
	{
		d[p<<1]+=b[p]*(m-s+1),d[(p<<1)|1]+=b[p]*(t-m);
		b[p<<1]+=b[p],b[(p<<1)|1]+=b[p];
	}
	b[p]=0;
	if(l<=m) update(l,r,c,s,m,p<<1);
	if(r>m) update(l,r,c,m+1,t,(p<<1)|1);
	d[p]=d[p<<1]+d[(p<<1)|1];
}

//another vision(+mul)
#define lson (p<<1)
#define rson ((p<<1)|1)
#define mid ((l+r)>>1)
int a[1000005];
int n,m,mod;
struct seg{
	int l,r,add,mul,sum;
}t[1000005];
inline void push_down(int p)
{
	t[rson].sum=((t[p].mul*t[rson].sum)+((t[rson].r-t[rson].l+1)*t[p].add)%mod)%mod;
	t[lson].sum=((t[p].mul*t[lson].sum)+((t[lson].r-t[lson].l+1)*t[p].add)%mod)%mod;
	
	t[rson].mul=(t[rson].mul*t[p].mul)%mod;
	t[lson].mul=(t[lson].mul*t[p].mul)%mod;
	
	t[rson].add=(t[rson].add*t[p].mul+t[p].add)%mod;
	t[lson].add=(t[lson].add*t[p].mul+t[p].add)%mod;
	
	t[p].add=0;t[p].mul=1;
}
inline void build(int l,int r,int p)
{
	t[p].l=l;t[p].r=r;t[p].mul=1;
	if(l==r)
	{
		t[p].sum=a[l]%mod;
		return;
	}
	build(l,mid,lson),build(mid+1,r,rson);
	t[p].sum=t[lson].sum+t[rson].sum;
}
inline void add(int l,int r,int k,int p)
{
	if(l<=t[p].l&&t[p].r<=r)
	{
		t[p].add=(t[p].add+k)%mod;
		t[p].sum=(t[p].sum+k*(t[p].r-t[p].l+1))%mod;
		return ;
	}
	
	push_down(p);
	t[p].sum=(t[lson].sum+t[rson].sum)%mod;
	int m=(t[p].l+t[p].r)>>1;
	if(l<=m) add(l,r,k,lson);
	if(r>m) add(l,r,k,rson);
	t[p].sum=(t[lson].sum+t[rson].sum)%mod;
}
inline void mul(int l,int r,int k,int p)
{
	if(l<=t[p].l&&t[p].r<=r)
	{
		t[p].add=(t[p].add*k)%mod;
		t[p].mul=(t[p].mul*k)%mod;
		t[p].sum=(t[p].sum*k)%mod;
		return ;
	}
	
	push_down(p);
	t[p].sum=(t[lson].sum+t[rson].sum)%mod;
	
	int m=(t[p].l+t[p].r)>>1;
	if(l<=m) mul(l,r,k,lson);
	if(r>m) mul(l,r,k,rson);
	t[p].sum=(t[lson].sum+t[rson].sum)%mod;
}
inline int getsum(int l,int r,int p)
{
	if(l<=t[p].l&&t[p].r<=r) return t[p].sum;
	
	push_down(p);
	int sum=0;
	
	int m=(t[p].l+t[p].r)>>1;
	if(l<=m) sum=(sum+getsum(l,r,lson))%mod;
	if(r>m) sum=(sum+getsum(l,r,rson))%mod;
	return sum;
}

pretree

第一行包含两个整数,分别表示序列的长度 \(n\) 和查询的个数 \(m\)

第二行包含 \(n\) 个整数,第 \(i\)个整数表示序列的第 \(i\)个元素 \(a_i\)
接下来 \(m\) 行每行包含三个整数$ l, r, k$ , 表示查询区间 \([l, r]\) 内的第 \(k\)小值。

#define mid ((l+r)>>1)
const int N=200100;
struct hjt{
	int ls,rs,cnt;
}t[N<<5];
int tot,rt[N];
int n,m;
int a[200005],b[200005];
int ln;
inline void lisan()
{
	sort(b+1,b+n+1);
	ln=unique(b+1,b+n+1)-b-1;
}
inline int mp(int x)
{
    return lower_bound(b+1,b+ln+1,x)-b;
}
inline void push_up(int p)
{
	t[p].cnt=t[t[p].ls].cnt+t[t[p].rs].cnt;
}
inline void update(int &id,int pre,int l,int r,int pos)
{
	id=++tot;
	t[id]=t[pre];
	++t[id].cnt;
	if(l==r)
	{
		return ;
	}
	if(mid<pos) update(t[id].rs,t[pre].rs,mid+1,r,pos);
	else update(t[id].ls,t[pre].ls,l,mid,pos);
	push_up(id);
}
inline int query(int id,int pre,int l,int r,int k)
{
	if(l==r) return b[l];//b[l];
	int dert=t[t[id].ls].cnt-t[t[pre].ls].cnt;
	if(dert<k) return query(t[id].rs,t[pre].rs,mid+1,r,k-dert);
	else return query(t[id].ls,t[pre].ls,l,mid,k);
}
//------------------------------------
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];//scanf("%d",&b[i]),a[i]=b[i];
	}
	lisan();
	for(int i=1;i<=n;++i) update(rt[i],rt[i-1],1,ln,mp(a[i]));
	while(m--)
	{
		int l,r,k;
		scanf("%d%d%d",&l,&r,&k);
		printf("%d\n",query(rt[r],rt[l-1],1,ln,k));
	}

字符串

Trie-1

struct trie {
  int nex[100000][26], cnt;
  bool exist[100000];  // 该结点结尾的字符串是否存在

  void insert(char *s, int l) {  // 插入字符串
    int p = 0;
    for (int i = 0; i < l; i++) {
      int c = s[i] - 'a';
      if (!nex[p][c]) nex[p][c] = ++cnt;  // 如果没有,就添加结点
      p = nex[p][c];
    }
    exist[p] = 1;
  }
  bool find(char *s, int l) {  // 查找字符串
    int p = 0;
    for (int i = 0; i < l; i++) {
      int c = s[i] - 'a';
      if (!nex[p][c]) return 0;
      p = nex[p][c];
    }
    return exist[p];
  }
};

Trie-2(Xor)

const int N=1000000;
struct edge{
	int v,w,next;
}e[N<<1];
int head[N],cnt;
void adde(int u,int v,int w)
{
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].next=head[u];
	head[u]=cnt;
}
int ch[N<<5][2],dis[N],tot=1;
void insert(int x)
{
	int p=1;
	for(int i=30;i>=0;--i)
	{
		int c=(x>>i)&1;
		if(!ch[p][c]) ch[p][c]=++tot;
		p=ch[p][c];
	}
}
int cal(int x)
{
	int res=0,p=1;
	for(int i=30;i>=0;--i)
	{
		int c=(x>>i)&1;
		if(ch[p][c^1]) p=ch[p][c^1],res|=(1<<i);
		else p=ch[p][c];
	}
	return res;
}
int ans;
void dfs(int u,int anc)
{
	insert(dis[u]);
	ans=max(cal(dis[u]),ans);
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==anc) continue;
		dis[v]=dis[u]^e[i].w;
		dfs(v,u);
	}
}
int n,m,u,v,w;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;++i)
	{
		scanf("%d%d%d",&u,&v,&w);
		adde(u,v,w);
		adde(v,u,w);
	}
	dfs(1,0);
	printf("%d",ans);
 	return 0;
}

KMP

定义一个字符串 \(s\) 的 border 为 \(s\) 的一个非 \(s\) 本身的子串 \(t\),满足 \(t\) 既是 \(s\) 的前缀,又是 \(s\) 的后缀。

首先输出若干行,每行一个整数,按从小到大的顺序输出\(s_2\)\(s_1\)中出现的位置。

最后一行输出\(|s_2|\)个整数,第\(i\)个整数表示\(s_2\)的长度为 \(i\)的前缀的最长 border 长度。

#include<bits/stdc++.h>
using namespace std;
int next[1000005];
char s[1000005],t[1000005];
int main()
{
	scanf("%s%s",s+1,t+1);//cin>>s+1>>t+1;
	next[1]=0;
	int n=strlen(s+1),m=strlen(t+1);
	int i=2,j=0;
	while(i<=m)//先和自己匹配 
	{
		while(j&&t[j+1]!=t[i]) j=next[j];
		if(t[j+1]==t[i]) ++j;
		next[i]=j;
		++i;
	}
	i=1;
	j=0;
	while(i<=n)
	{
		while(j&&t[j+1]!=s[i]) j=next[j];
		if(t[j+1]==s[i]) ++j;
		if(j==m)
		{
			printf("%d\n",i-j+1);
			j=next[j];
		}
		++i;
	}
	i=1;
	while(i<=m) printf("%d ",next[i++]);
 	return 0;
}
posted @ 2020-11-25 17:00  子漫  阅读(67)  评论(0编辑  收藏  举报