模板收集
由于找到的模板大都不全,于是决定自己写
优化类
快读
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;
}