模板整理

一世执手 提交于 2019-12-01 05:47:43

前言

众所周知,一些算法和数据结构是很板子化的,如果我们要用需要先记住他(这不废话吗)
2019.10.8

线段树(区间加与区间求和)

注意右子树区间大小为\(r-(mid+1)+1=r-mid\)

#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define re register #define int long long #define maxn 100010 #define ls p<<1 #define rs p<<1|1 #define size (r-l+1)  using namespace std;  inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } int ans; int n,m,sums[maxn<<2],lazy[maxn<<2],opt,x,y,k,a[maxn]; void push_up(int p) {     sums[p]=sums[ls]+sums[rs]; } void push_down(int p,int l,int r) {     int mid=(l+r)>>1;     lazy[ls]+=lazy[p];     lazy[rs]+=lazy[p];     sums[ls]+=(mid-l+1)*lazy[p];     sums[rs]+=(r-mid)*lazy[p];     lazy[p]=0; } void build(int l,int r,int p) {     if(l==r)     {         sums[p]=a[l];         return ;     }     int mid=(l+r)>>1;     build(l,mid,ls);     build(mid+1,r,rs);     push_up(p);  } void modify(int l,int r,int ql,int qr,int v,int p) {     if(ql<=l&&r<=qr)     {         sums[p]+=size*v;         lazy[p]+=v;         return;     }     push_down(p,l,r);     int mid=(l+r)>>1;     if(ql<=mid) modify(l,mid,ql,qr,v,ls);     if(qr>mid) modify(mid+1,r,ql,qr,v,rs);     push_up(p); } void query(int l,int r,int ql,int qr,int p) {     if(ql<=l&&r<=qr)     {         ans+=sums[p];         return;      }     push_down(p,l,r);     int mid=(l+r)>>1;     if(ql<=mid) query(l,mid,ql,qr,ls);     if(qr>mid) query(mid+1,r,ql,qr,rs); } signed main() { //  freopen("1.in","r",stdin);     n=read(),m=read();     for(re int i=1;i<=n;++i) a[i]=read();     build(1,n,1);     for(re int i=1;i<=m;++i)     {         opt=read();         if(opt==1)         {             x=read(),y=read(),k=read();             modify(1,n,x,y,k,1);         }         else         {             x=read(),y=read();             ans=0;             query(1,n,x,y,1);             printf("%lld\n",ans);         }     }     return 0; }

线段树(区间乘、区间加、区间求和)
注意下传标记时先处理乘法标记:

先把子节点加法标记乘要下传的乘法标记,然后子节点乘法标记 乘 乘法标记(乘法比加法运算级优先)乘法标记初始化为1

再处理加法标记,在\(modify2\)函数中也要这样做

#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define re register #define int long long #define maxn 100010 #define ls p<<1 #define rs p<<1|1 #define size (r-l+1)  using namespace std;  inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } int ans; int lazy2[maxn<<2],mod; int n,m,sums[maxn<<2],lazy[maxn<<2],opt,x,y,k,a[maxn]; void push_up(int p) {     sums[p]=sums[ls]+sums[rs];     sums[p]%=mod; } void push_down(int p,int l,int r) {      int mid=(l+r)>>1;      lazy2[ls]=(lazy2[p]*lazy2[ls])%mod;      lazy2[rs]=(lazy2[p]*lazy2[rs])%mod;      lazy[ls]=(lazy[ls]*lazy2[p])%mod;      lazy[rs]=(lazy[rs]*lazy2[p])%mod;      sums[ls]=(sums[ls]*lazy2[p])%mod;      sums[rs]=(sums[rs]*lazy2[p])%mod;      lazy2[p]=1;      lazy[ls]=(lazy[ls]+lazy[p])%mod;      lazy[rs]=(lazy[rs]+lazy[p])%mod;      sums[ls]=(sums[ls]+(mid-l+1)*lazy[p])%mod;      sums[rs]=(sums[rs]+(r-mid)*lazy[p])%mod;      lazy[p]=0; } void build(int l,int r,int p) {     if(l==r)     {         lazy2[p]=1;         sums[p]=a[l];         return ;     }     lazy2[p]=1;     int mid=(l+r)>>1;     build(l,mid,ls);     build(mid+1,r,rs);     push_up(p);  } void modify(int l,int r,int ql,int qr,int v,int p) {     if(ql<=l&&r<=qr)     {         sums[p]+=size*v;         sums[p]%=mod;         lazy[p]+=v;         lazy[p]%=mod;         return;     }     if((lazy2[p]!=1)||lazy[p]) push_down(p,l,r);     int mid=(l+r)>>1;     if(ql<=mid) modify(l,mid,ql,qr,v,ls);     if(qr>mid) modify(mid+1,r,ql,qr,v,rs);     push_up(p); } void modify2(int l,int r,int ql,int qr,int v,int p) {     if(ql<=l&&r<=qr)     {         sums[p]*=v;         sums[p]%=mod;         lazy[p]*=v;         lazy[p]%=mod;         lazy2[p]*=v;         lazy2[p]%=mod;          return;     }        if((lazy2[p]!=1)||lazy[p]) push_down(p,l,r);      int mid=(l+r)>>1;     if(ql<=mid) modify2(l,mid,ql,qr,v,ls);     if(qr>mid) modify2(mid+1,r,ql,qr,v,rs);     push_up(p); } void query(int l,int r,int ql,int qr,int p) {     if(ql<=l&&r<=qr)     {         ans+=sums[p];         ans%=mod;         return;      }     if((lazy2[p]!=1)||lazy[p]) push_down(p,l,r);     int mid=(l+r)>>1;     if(ql<=mid) query(l,mid,ql,qr,ls);     if(qr>mid) query(mid+1,r,ql,qr,rs); }  signed main() {     //freopen("1.in","r",stdin);     n=read(),m=read(),mod=read();     for(re int i=1;i<=n;++i) a[i]=read();     build(1,n,1);     for(re int i=1;i<=m;++i)     {         opt=read();     //  printf("test:%d\n",sums[9]);         if(opt==1)         {             x=read(),y=read(),k=read();             modify2(1,n,x,y,k,1);         }         else if(opt==2)         {             x=read(),y=read(),k=read();             modify(1,n,x,y,k,1);         }         else         {             x=read(),y=read();             ans=0;             query(1,n,x,y,1);             printf("%lld\n",ans%mod);         }     }     return 0; }

卡时,对于某些非计数暴力可以使用这种方法,在程序超时之前输出答案,$TLE$0分,这样做有可能拿到\(10\)

(double)clock()/CLOCKS_PER_SEC>=0.67 具体写法: if((double)clock()/CLOCKS_PER_SEC>=0.67) exit(0);

2019.10.9

倍增求\(LCA\),注意倍增可以预处理\(lg\)数组进行常数优化

#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #define re register #define ll long long #define maxn 500100 using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } struct Edge{     int v,nxt; }e[maxn<<2]; int lg[maxn],num; int n,m,s,cnt,dep[maxn],head[maxn],f[maxn][25],x,y; inline void add(int u,int v) {     e[++cnt].v=v;     e[cnt].nxt=head[u];     head[u]=cnt; } void dfs(int u,int fa) {     dep[u]=dep[fa]+1;     f[u][0]=fa;     for(int i=1;(1<<i)<=dep[u];++i)     f[u][i]=f[f[u][i-1]][i-1];     for(int i=head[u];i;i=e[i].nxt)     {         int v=e[i].v;         if(v==fa) continue;         dfs(v,u);     } } int lca(int x,int y) {     if(dep[y]>dep[x]) swap(x,y);     for(int i=lg[dep[x]-dep[y]];i>=0;--i)     {         if(dep[f[x][i]]<dep[y]) continue;         x=f[x][i];     }     if(x==y) return x;     for(int i=lg[dep[x]-1];i>=0;--i)     {         if(f[x][i]==f[y][i]) continue;         x=f[x][i],y=f[y][i];     }     return f[x][0]; } int main() {     n=read(),m=read(),s=read();     for(re int i=1;i<n;++i)     {         x=read(),y=read();         add(x,y);         add(y,x);     }     dfs(s,0);     int tmp=1;     while(tmp<=n)     {         lg[tmp]=num;         num++;         tmp*=2;     }     for(re int i=1;i<=n;++i)     {         if(lg[i]) continue;         lg[i]=lg[i-1];     }     for(re int i=1;i<=m;++i)     {         x=read(),y=read();         printf("%d\n",lca(x,y));     }     return 0; }

\(Dijkstra\)堆优化

#include<cstdio> #include<iostream> #include<queue> #define re register #define maxn 200010 using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } struct Edge{     int v,w,nxt; }e[maxn<<2]; int a,b,c,s; int cnt,head[maxn],vis[maxn],n,m,dis[maxn]; inline void add(int u,int v,int w) {     e[++cnt].v=v;     e[cnt].w=w;     e[cnt].nxt=head[u];     head[u]=cnt; } int ev; struct node{     int u,d;     bool operator <(const node&rhs)const {         return rhs.d<d;     } }; void dijkstra() {     priority_queue<node> q;     dis[s]=0;     q.push((node){s,0});     while(!q.empty())     {         node f=q.top();         q.pop();         int now=f.u,dd=f.d;         if(vis[now]) continue;         vis[now]=1;         for(int i=head[now];i;i=e[i].nxt)         {             ev=e[i].v;             if(dis[ev]>dis[now]+e[i].w)             {                 dis[ev]=dis[now]+e[i].w;                 if(!vis[ev])                 {                     q.push((node){ev,dis[ev]});                 }             }         }      }      } int main() {     n=read(),m=read(),s=read();     for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;     for(re int i=1;i<=m;++i)     {         a=read(),b=read(),c=read();         add(a,b,c);     }     dijkstra();     for(re int i=1;i<=n;++i)     printf("%d ",dis[i]);     return 0; }

\(SPFA\)

#include<cstdio> #include<iostream> #include<queue> #define re register #define maxn 200010 using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } struct Edge{     int v,w,nxt; }e[maxn<<2]; int a,b,c,s; int cnt,head[maxn],vis[maxn],n,m,dis[maxn]; inline void add(int u,int v,int w) {     e[++cnt].v=v;     e[cnt].w=w;     e[cnt].nxt=head[u];     head[u]=cnt; } int ev,eu; void spfa() {     dis[s]=0;     queue<int> q;     q.push(s);     vis[s]=true;     while(!q.empty())     {         eu=q.front();         q.pop();         vis[eu]=false;         for(int i=head[eu];i;i=e[i].nxt)         {             ev=e[i].v;             if(dis[ev]>dis[eu]+e[i].w)             {                 dis[ev]=dis[eu]+e[i].w;                 if(!vis[ev])                 {                     vis[ev]=true;                     q.push(ev);                 }             }         }     }  } int main() {     n=read(),m=read(),s=read();     for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;     for(re int i=1;i<=m;++i)     {         a=read(),b=read(),c=read();         add(a,b,c);     }     spfa();     for(re int i=1;i<=n;++i)     printf("%d ",dis[i]);     return 0; }

双端队列优化\(SPFA\)

#include<cstdio> #include<iostream> #include<queue> #define re register #define maxn 200010 using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } struct Edge{     int v,w,nxt; }e[maxn<<2]; int a,b,c,s; int cnt,head[maxn],vis[maxn],n,m,dis[maxn]; inline void add(int u,int v,int w) {     e[++cnt].v=v;     e[cnt].w=w;     e[cnt].nxt=head[u];     head[u]=cnt; } int ev,eu; void spfa() {     dis[s]=0;     deque<int> q;     q.push_back(s);     vis[s]=true;     while(!q.empty())     {         eu=q.front();         q.pop_front();         vis[eu]=false;         for(int i=head[eu];i;i=e[i].nxt)         {             ev=e[i].v;             if(dis[ev]>dis[eu]+e[i].w)             {                 dis[ev]=dis[eu]+e[i].w;                 if(!vis[ev])                 {                     vis[ev]=1;                     if(q.empty()||dis[ev]<dis[q.front()]) q.push_front(ev);                     else q.push_back(ev);                 }             }         }     }  } int main() {     n=read(),m=read(),s=read();     for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;     for(re int i=1;i<=m;++i)     {         a=read(),b=read(),c=read();         add(a,b,c);     }     spfa();     for(re int i=1;i<=n;++i)     printf("%d ",dis[i]);     return 0; }

堆优化\(SPFA\)

#include<cstdio> #include<iostream> #include<queue> #define re register #define maxn 200010 using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } struct Edge{     int v,w,nxt; }e[maxn<<2]; int a,b,c,s; int cnt,head[maxn],vis[maxn],n,m,dis[maxn]; inline void add(int u,int v,int w) {     e[++cnt].v=v;     e[cnt].w=w;     e[cnt].nxt=head[u];     head[u]=cnt; } struct cmp{     bool operator ()(int &x,int &y)     {         return dis[x]>dis[y];     } }; int ev,eu; void spfa() {     dis[s]=0;     priority_queue<int,vector<int>,cmp> q;     q.push(s);     vis[s]=true;     while(!q.empty())     {         eu=q.top();         q.pop();         vis[eu]=false;         for(int i=head[eu];i;i=e[i].nxt)         {             ev=e[i].v;             if(dis[ev]>dis[eu]+e[i].w)             {                 dis[ev]=dis[eu]+e[i].w;                 if(!vis[ev])                 {                     vis[ev]=1;                      q.push(ev);                 }             }         }     }  } int main() {     n=read(),m=read(),s=read();     for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;     for(re int i=1;i<=m;++i)     {         a=read(),b=read(),c=read();         add(a,b,c);     }     spfa();     for(re int i=1;i<=n;++i)     printf("%d ",dis[i]);     return 0; }

10.10

割点

#include<iostream> #include<cstdio> #include<cstring> #define re register #define maxn 200010 #define maxm 500020 using namespace std; int n,m,x,y,cnt,head[maxn],ans; int flag[maxn],Index,DFN[maxn],LOW[maxn]; struct Edge{     int v,nxt; }e[maxm<<2]; inline void add(int u,int v) {     e[++cnt].v=v;     e[cnt].nxt=head[u];     head[u]=cnt;  } void dfs(int x,int fa) {     int child=0;     DFN[x]=LOW[x]=++Index;     for(int i=head[x];i;i=e[i].nxt)     {         int ev=e[i].v;         if(!DFN[ev])         {             dfs(ev,fa);             LOW[x]=min(LOW[x],LOW[ev]);             if(LOW[ev]>=DFN[x]&&x!=fa) flag[x]=1;                          if(x==fa) child++;         }         LOW[x]=min(LOW[x],DFN[ev]);     }     if(child>=2&&x==fa) flag[x]=1; } int main() {     scanf("%d%d",&n,&m);     for(re int i=1;i<=m;++i)     {         scanf("%d%d",&x,&y);         add(x,y);         add(y,x);     }     for(re int i=1;i<=n;++i)      if(!DFN[i]) dfs(i,i);     for(re int i=1;i<=n;++i)     {         if(flag[i]) ans++;     }      printf("%d\n",ans);     for(re int i=1;i<=n;++i)     {         if(flag[i]) printf("%d ",i);     } } 

对于语句

LOW[x]=min(LOW[x],DFN[ev])的解释 

\(tarjan\)缩点
主要是缩点后对处理后的图进行其他操作

#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn =1e5+1e4, maxm=1e5+1e4; int Index,vis[maxn],num[maxn],low[maxn]; int tot,color[maxn],sum[maxn],f[maxn]; int edge,head[maxn],next[maxm],to[maxm]; int stack[maxn],top;  int n,m,val[maxn],x[maxm],y[maxm],ans; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } void add(int u,int v) {       to[++edge]=v;        next[edge]=head[u];       head[u]=edge; } void tarjan(int x) {     low[x]=num[x]=++Index;     stack[++top]=x;     vis[x]=1;     for(int i=head[x];i;i=next[i])     {         int v=to[i];         if(!num[v])         {             tarjan(v);             low[x]=min(low[x],low[v]);         }         else if(vis[v])         {             low[x]=min(low[x],low[v]);         }     }     if(low[x]==num[x])     {         tot++;         while(stack[top+1]!=x)         {             color[stack[top]]=tot;             sum[tot]+=val[stack[top]];             vis[stack[top--]]=0;         }     } } void search(int x) {     if(f[x]) return;     f[x]=sum[x];     int maxsum=0;     for(int i=head[x];i;i=next[i])     {         int v=to[i];         if(!f[v]) search(v);         maxsum=max(maxsum,f[v]);     }     f[x] +=maxsum; } int main() {      n=read(); m=read();      for(register int i=1;i<=n;i++) val[i]=read();      for(register int i=1;i<=m;i++)      {            x[i]=read();            y[i]=read();            add(x[i],y[i]);      }      for(register int i=1;i<=n;i++) if(!num[i]) tarjan(i);      memset(head,0,sizeof(head));      memset(next,0,sizeof(next));      memset(to,0,sizeof(to));      edge=0;      for(register int i=1;i<=m;i++)      {         if(color[x[i]]!=color[y[i]])           add(color[x[i]],color[y[i]]);      }      for(register int i=1;i<=tot;i++)      {          if(!f[i])          {             search(i);             ans=max(ans,f[i]);          }      }      printf("%d",ans);      return 0; }

树链剖分

树链剖分是方法,配合线段树等才是算法

#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define re register #define maxn 200010 using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; }  int id[maxn],res; int son[maxn],maxson,ans,sums[maxn<<2],lazy[maxn<<2]; int w[maxn],wt[maxn],cnt,top[maxn],fa[maxn],dep[maxn]; int mod,n,m,root,opt,x,y,z,head[maxn],cnts,siz[maxn]; struct Edge{     int v,nxt; }e[maxn<<2]; inline void add(int u,int v) {     e[++cnts].v=v;     e[cnts].nxt=head[u];     head[u]=cnts; } //-------------线段树--------------- #define ls p<<1 #define rs p<<1|1 #define size (r-l+1) void push_down(int p,int l,int r) {     lazy[p]%=mod;     lazy[ls]=(lazy[p]+lazy[ls])%mod;     lazy[rs]=(lazy[p]+lazy[rs])%mod;     int mid=(l+r)>>1;     sums[ls]=(sums[ls]+lazy[p]*(mid-l+1))%mod;     sums[rs]=(sums[rs]+lazy[p]*(r-mid))%mod;     lazy[p]=0; } void push_up(int p){sums[p]=(sums[ls]+sums[rs])%mod;} void build(int l,int r,int p) {     if(l==r)     {         sums[p]=wt[l];         return;     }     int mid=(l+r)>>1;     build(l,mid,ls);     build(mid+1,r,rs);     push_up(p); }   void update(int p,int l,int r,int ql,int qr,int k) {     if(ql<=l&&r<=qr)      {         sums[p]=(sums[p]+k*size)%mod;         lazy[p]=(lazy[p]+k)%mod;         return;     }     push_down(p,l,r);     int mid=(l+r)>>1;     if(ql<=mid) update(ls,l,mid,ql,qr,k);     if(qr>mid) update(rs,mid+1,r,ql,qr,k);     push_up(p); } void query(int p,int l,int r,int ql,int qr) {     if(ql<=l&&r<=qr)     {         res=(sums[p]+res)%mod;         return;     }     push_down(p,l,r);     int mid=(l+r)>>1;     if(ql<=mid) query(ls,l,mid,ql,qr);     if(qr>mid) query(rs,mid+1,r,ql,qr); } //-------------线段树---------------  void dfs1(int u,int fat,int deep) {     dep[u]=deep;     fa[u]=fat;     siz[u]=1;     maxson=-1;     for(int i=head[u];i;i=e[i].nxt)     {         int ev=e[i].v;         if(ev==fat) continue;         dfs1(ev,u,deep+1);         siz[u]+=siz[ev];         if(siz[ev]>maxson) maxson=siz[ev],son[u]=ev;     } } void dfs2(int u,int topf) {     id[u]=++cnt;     wt[cnt]=w[u];     top[u]=topf;     if(!son[u]) return;     dfs2(son[u],topf);     for(int i=head[u];i;i=e[i].nxt)     {         int ev=e[i].v;         if(ev==fa[u]) continue;         if(son[u]==ev) continue;         dfs2(ev,ev);     } } void addRange(int x,int y,int z) {     z%=mod;//这里别忘取模      while(top[x]!=top[y])//不在一条链上,别写成top[y]!=x      {         if(dep[top[x]]<dep[top[y]]) swap(x,y);//注意让x的顶端深度而不是x的深度是两者中最大的         update(1,1,n,id[top[x]],id[x],z);         x=fa[top[x]];     }     if(dep[y]>dep[x]) swap(x,y);     update(1,1,n,id[y],id[x],z);      } void addSon(int x,int z) {     z%=mod;     update(1,1,n,id[x],id[x]+siz[x]-1,z); } void qSon(int x) {     res=0;     query(1,1,n,id[x],id[x]+siz[x]-1);     ans=(ans+res)%mod; } void qRange(int x,int y) {     while(top[x]!=top[y])     {         if(dep[top[x]]<dep[top[y]]) swap(x,y);         res=0;         query(1,1,n,id[top[x]],id[x]);         ans=(ans+res)%mod;         x=fa[top[x]];     }     if(dep[y]>dep[x]) swap(x,y);     res=0;     query(1,1,n,id[y],id[x]);     ans=(ans+res)%mod; } int main() {     n=read(),m=read(),root=read(),mod=read();     for(re int i=1;i<=n;++i) w[i]=read();     for(re int i=1;i<n;++i)     {         x=read(),y=read();         add(x,y);         add(y,x);     }     dfs1(root,0,1);     dfs2(root,root);     build(1,n,1);     for(re int i=1;i<=m;++i)     {         opt=read();         if(opt==1)         {             x=read(),y=read(),z=read();             addRange(x,y,z);         }         else if(opt==2)         {             x=read(),y=read();             ans=0;             qRange(x,y);             printf("%d\n",ans);         }         else if(opt==3)         {             x=read(),z=read();             addSon(x,z);         }         else         {             x=read();             ans=0;             qSon(x);             printf("%d\n",ans);         }     }     return 0; }

树状数组1(单点修改,区间查询)

#include<cstdio> #include<iostream> #include<queue> #define maxn 2000010 #define lowbit(x) x&(-x) //#define int long long #define re register using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } int s[maxn<<1]; int n,m,x,y,k,opt; inline void add(int x,int k) {     while(x<=n)     {         s[x]+=k;         x+=lowbit(x);     } } inline int query(int x) {     int ans=0;     while(x>0)     {         ans+=s[x];         x-=lowbit(x);     }     return ans; } int main() {     n=read(),m=read();     for(re int i=1;i<=n;++i)     {         x=read();         add(i,x);     }     for(re int i=1;i<=m;++i)     {         opt=read(),x=read(),k=read();         if(opt==1)  add(x,k);         else  printf("%d\n",query(k)-query(x-1));     }     return 0; }

树状数组2(区间修改单点查询)

思想:树状数组维护的是前缀和,因此直接加入差分数组求前缀和就是原数组

#include<cstdio> #include<iostream> #include<queue> #define int long long #define maxn 2000010 #define lowbit(x) x&(-x) //#define int long long #define re register using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } int s[maxn<<1]; int n,m,x,y,k,opt; inline void add(int x,int k) {     while(x<=n)     {         s[x]+=k;         x+=lowbit(x);     } } inline int query(int x) {     int ans=0;     while(x>0)     {         ans+=s[x];         x-=lowbit(x);     }     return ans; } int last; signed main() {     n=read(),m=read();     for(re int i=1;i<=n;++i)     {         x=read();         add(i,x-last);//巨坑,注意加入的数是差分数组         last=x;     }     for(re int i=1;i<=m;++i)     {         opt=read();         if(opt==1)         {             x=read(),y=read(),k=read();             add(x,k);             add(y+1,-k);         }            else          {             x=read();             printf("%lld\n",query(x));         }     }     return 0; }

10.12

最小生成树\(prim\)

基于\(dijkstra\)的思想,稠密图更优

#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define INF 0x3f3f3f3f #define re register #define maxn 50010 #define maxm 200010  using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } int n,m,ans,minn,now,vis[maxn],head[maxn],x,y,z,cnt,tot; int dis[maxn],ev; struct Edge{     int v,w,nxt; }e[maxm<<2];//坑,数组开边的大小的两倍不是点的大小  inline void add(int u,int v,int w) {     e[++cnt].v=v;     e[cnt].w=w;     e[cnt].nxt=head[u];     head[u]=cnt; } inline int prim() {     memset(dis,INF,sizeof(dis));     dis[1]=0;     for(re int i=head[1];i;i=e[i].nxt)     {         ev=e[i].v;         dis[ev]=min(dis[ev],e[i].w);     }     now=1;     while(++tot<n)     {         minn=INF;         vis[now]=1;         for(re int i=1;i<=n;++i)         {             if(!vis[i]&&minn>dis[i])             {                 minn=dis[i];                 now=i;             }         }         ans+=minn;         for(re int i=head[now];i;i=e[i].nxt)         {             ev=e[i].v;//注意这里找的不是到源点的最短路而是到已经访问边集的最短路             //dis表示的是到当前边集,因此dis[ev]>e[i].w而不是             //dis[ev]>dis[now]+e[i].w              if(dis[ev]>e[i].w&&!vis[ev])             {                 dis[ev]=e[i].w;             }         }     }     return ans; } int main() {     n=read(),m=read();     for(re int i=1;i<=m;++i)     {         x=read(),y=read(),z=read();         add(x,y,z);         add(y,x,z);     }     printf("%d\n",prim());     return 0; }

最小生成树\(kruskal\)(附带按秩合并、路径压缩模板)

写起来更简单,稀疏图快

#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define INF 0x3f3f3f3f #define re register #define maxn 5050 #define maxm 200010 using namespace std; inline int read() {     int x=0,f=1; char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } struct Edge{     int u,v,w; }e[maxm<<2]; int ev,eu,ans,fa[maxn],n,m,cnt,ranks[maxn]; int find(int x) {     return fa[x]==x?x:fa[x]=find(fa[x]); } void merge(int x,int y) {     //c++11中rank是函数      //ranks[i]表示以i为根的树的树高      //注意只有两个树rank相等时总ranks才++      if(ranks[y]>ranks[x])     {         fa[x]=y;     }     else     {         fa[y]=x;         if(x==y) ranks[x]++;     } } void pre() {     for(re int i=1;i<=n;++i) fa[i]=i;     ans=0; } bool cmp(Edge A,Edge B) {     return A.w<B.w; }  inline int kruskal() {     pre();     sort(e+1,e+m+1,cmp);     for(re int i=1;i<=m;++i)     {         eu=find(e[i].u),ev=find(e[i].v);         if(eu==ev) continue;         merge(ev,eu);//将父亲合并          ans+=e[i].w;         if(++cnt==n-1) break;     }     return ans; } int main() {     n=read(),m=read();     for(re int i=1;i<=m;++i)     {         e[i].u=read(),e[i].v=read(),e[i].w=read();     }     printf("%d\n",kruskal());     return 0; }

大随机数生成

\(c++11\),开完一定记得关上,避免使用\(c++11\)的东西导致\(CE\)

命令:\(-std=c++11\)

#include<random>//注意 #include<ctime> using namespace std; mt19937 rnd(time(0)); int main() {     printf("%lld\n",rnd());     return 0; }

区间查询区间修改树状数组

比线段树常数小,注意一开始加入的数是差分数组

void add(ll p, ll x){     for(int i = p; i <= n; i += i & -i)         sum1[i] += x, sum2[i] += x * p; } void range_add(ll l, ll r, ll x){     add(l, x), add(r + 1, -x); } ll ask(ll p){     ll res = 0;     for(int i = p; i; i -= i & -i)         res += (p + 1) * sum1[i] - sum2[i];     return res; } ll range_ask(ll l, ll r){     return ask(r) - ask(l - 1); }

二维树状数组

相当于多加一维,先把\(y\)当做一维考虑,求和或者修改,在用同样的方法在\(x\)轴上扩展(相当于把\(y\)轴的信息压缩成一维再搞)。

求和需要用到二维前缀和,容斥即可


单点修改区间查询

#include<cstdio> #include<iostream> #define re register #define maxn 1010 using namespace std; int opt; int tree[maxn][maxn],n,x,y,k,a,b,c,d,m; void add(int x,int y,int z) {     int memo_y=y;     while(x<=n)     {         y=memo_y;         while(y<=n)           tree[x][y]+=z,y+=(y&-y);         x+=(x&-x);     } } int ask(int x,int y) {     int res=0,memo_y=y;     while(x)     {         y=memo_y;         while(y)          res+=tree[x][y],y-=(y&-y);         x-=(x&-x);     }     return res; } int query(int a,int b,int c,int d)//询问左上角(a,b),右下角(c,d) {     return ask(c,d)-ask(c,b-1)-ask(a-1,d)+ask(a-1,b-1); } int main() {     scanf("%d%d",&n,&m);     for(re int i=1;i<=m;++i)     {         scanf("%d",&opt);         if(opt==1)         {             scanf("%d%d%d",&x,&y,&k);             add(x,y,k);         }         else         {             scanf("%d%d%d%d",&a,&b,&c,&d);             printf("%d\n",query(a,b,c,d));         }     }     return 0; }

区间修改单点查询

同样道理,对差分数组求前缀和

二维前缀和的公式是这样的:

sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];

又因为普通数组是对差分数组求前缀和
可以推出

    a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+d[i][j];     d[i][j]=a[i][j]+a[i-1][j-1]-a[i-1][j]-a[i][j-1];

假设我们要加入的数组是这样的

    0 0 0 0 0     0 0 0 0 0     0 1 1 1 0     0 1 1 1 0     0 1 1 1 0     0 0 0 0 0

那么显然差分数组这样加入

    0 0 0 0 0     0 0 0 0 0     0 1 0 0 -1      0 0 0 0 0     0 0 0 0 0     0 -1 0 0 1

其实感性理解一下就是两个方向分别作一维差分

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!