前言
众所周知,一些算法和数据结构是很板子化的,如果我们要用需要先记住他(这不废话吗)
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
其实感性理解一下就是两个方向分别作一维差分