前言
因为经常考试,考着考着就开始气,明明想到/会做,但是模板不熟,或者哪个细节忘了,或者思想有点忘了,所以不敢写/写的很慢/写挂
所以要记录一下要复习的内容,还是以时间为序,完成时间会在以后用红字在后面添上。
复习大概是要包括 重温算法+熟练模板+写(不是板的)例题
lca
【happy那道题,想出来过后开心了半天,结果...敲不对】
模板(注解的地方是踩得雷)
poj1330
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 10100 int n,t,x,y,cnt,root; int first[N],in[N],dep[N]; int fa[N][20]; struct email { int u,v; int nxt; }e[N*4]; inline void add(int u,int v) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v; } void dfs(int u,int f) { for(int i=1;(1<<i)<=dep[u];i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(v==f)continue; fa[v][0]=u; dep[v]=dep[u]+1; dfs(v,u); } } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int k=dep[x]-dep[y]; for(int i=0;(1<<i)<=k;i++)//1-->0 if(k&(1<<i))//手滑了 (i<<1)-->(1<<i) x=fa[x][i]; if(x==y)return x; for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } inline void init() { memset(in,0,sizeof(in)); memset(dep,0,sizeof(dep)); memset(first,0,sizeof(first)); memset(fa,0,sizeof(fa)); cnt=0; } int main() { scanf("%d",&t); while(t--) { init();//不能省 scanf("%d",&n); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v);add(v,u);in[v]++; } for(int i=1;i<=n;i++) if(!in[i]) {root=i;break;} dfs(root,0); scanf("%d%d",&x,&y); printf("%d ",lca(x,y)); } }
hdu2586
#include<bits/stdc++.h> using namespace std; #define N 40400 int n,t,m,x,y,cnt; int first[N],dep[N],d[N]; int fa[N][20]; struct email { int u,v,w; int nxt; }e[N*4]; inline void add(int u,int v,int w) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].v=v;e[cnt].u=u;e[cnt].w=w; } inline void init() { memset(in,0,sizeof(in)); memset(dep,0,sizeof(dep)); memset(first,0,sizeof(first)); memset(fa,0,sizeof(fa)); memset(d,0,sizeof(d)); cnt=0; } void dfs(int u,int f) { for(int i=1;(1<<i)<=dep[u];i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v,w=e[i].w; if(v==f)continue; d[v]=d[u]+w; dep[v]=dep[u]+1; fa[v][0]=u; dfs(v,u); } } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int k=dep[x]-dep[y]; for(int i=0;(1<<i)<=k;i++) if(k&(1<<i)) x=fa[x][i]; if(x==y)return x; for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } int main() { scanf("%d",&t); while(t--) { init(); scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w);add(v,u,w);in[v]++; } dfs(1,0); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); printf("%d ",d[x]+d[y]-2*d[lca(x,y)]); } } }
不是板的例题:HDU4912
结束
tarjan
【也是因为happy那道题...看到环的第一想法,在脑子里默默过了一遍,发现好像不熟了?】
首先是比较熟的有向图的强连通分量模板
POJ2186 受欢迎的牛
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<stack> using namespace std; #define N 20020 int dfn[N],low[N],siz[N],col[N],vis[N],out[N],first[N]; int n,m,cnt,tot,gro,ans; stack<int>s; struct email { int u,v; int nxt; }e[N*5]; inline void add(int u,int v) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v; } void tarjan(int u) { dfn[u]=low[u]=++tot; s.push(u);vis[u]=1; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { ++gro; while(1) { int t=s.top();s.pop();vis[t]=0; siz[gro]++;col[t]=gro; if(t==u)break; } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); } for(int i=1;i<=n;i++) if(!col[i]) tarjan(i); for(int i=1;i<=m;i++) { int u=e[i].u,v=e[i].v; if(col[u]!=col[v]) out[col[u]]++; } for(int i=1;i<=gro;i++) { if(out[i]==0)//out[col[i]]==0; { if(ans) { ans=0; break; } ans=i; } } printf("%d ",siz[ans]); }
HDU 1269
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<stack> using namespace std; #define N 10100 #define M 100100 int vis[N],dfn[N],low[N],col[N],first[N]; int n,m,cnt,tot,gro,flag; stack<int>s; struct email { int u,v; int nxt; }e[M*2]; inline void add(int u,int v) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v; } inline void init() { cnt=0,tot=0,gro=0; memset(first,0,sizeof(first)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(vis,0,sizeof(vis)); memset(col,0,sizeof(col)); } void tarjan(int u) { dfn[u]=low[u]=++tot; s.push(u);vis[u]=1; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { ++gro; while(1) { int t=s.top();s.pop();vis[t]=0; col[t]=gro; if(t==u)break; } } } int main() { while(scanf("%d%d",&n,&m)&&n) { init(); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); } for(int i=1;i<=n;i++) if(!col[i]) tarjan(i); if(gro==1) printf("Yes "); else printf("No "); } }
难受啊,没想到割边割点对我来说比缩点更困难
终于割边的板子出来了
luogu 3388
给自己写一点reminders吧
1.根据定义,只有一个子节点的根不是割点,所以dfs时,分是根或者不是根,结束dfs当前节点前补回这个u可能是根且是割点的情况
2.割点数量最后统计,不要懒得打那一个for循,在过程中统计包挂
3.割点是>=
4.图不一定联通
5.不需要判v==fa,因为dfn能阻断已经访问过的点
6.如果v不能回到比dfn[u]小的点,说明u是割点,而不是v(只能回到u点,u也是割点)
#include<bits/stdc++.h> using namespace std; #define N 100100 int n,m,cnt,tot,num,root,child; int dfn[N],low[N],cut[N],first[N]; struct email { int u,v; int nxt; }e[N*4]; inline void add(int u,int v) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v; } inline void tarjan(int u) { dfn[u]=low[u]=++tot;child=0; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]&&u!=root) cut[u]=1;//,num++ if(u==root)child++; } else low[u]=min(low[u],dfn[v]); } if(child>1&&u==root)cut[u]=1;//,num++ } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v);add(v,u); } for(int i=1;i<=n;i++) if(!dfn[i]) root=i,tarjan(i); for(int i=1;i<=n;i++) if(cut[i]) num++; printf("%d ",num); for(int i=1;i<=n;i++) if(cut[i]) printf("%d ",i); return 0; }
今晚商量了一下停课的事情,割边终于也出炉了,tarjan这一块儿终于可以放心地做题了
没测过,只自己手造了一组,假装是对的吧【找不到模板题测啊】
又是可爱的reminders
1.这个不需要判根,但是需要严格防止搜爆,比起割点,多传参了一个u。
2.有一句 v!=fa&&dfn[v]<dfn[u],双重保险吧,我也不知道哪句必要,干脆都写
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100100 int dfn[N],low[N],first[N]; int n,m,cnt,tot,num; struct email { int u,v; int nxt; }e[N*4],cut[N*4]; inline void add(int u,int v) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v; } inline void tarjan(int u,int fa) { dfn[u]=low[u]=++tot; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v; if(!dfn[v]) { tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) cut[++num]=e[i]; } else if(v!=fa&&dfn[v]<dfn[u]) low[u]=min(low[u],dfn[v]); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v);add(v,u); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,0); for(int i=1;i<=num;i++) { int u=cut[i].u,v=cut[i].v; printf("%d-->%d ",u,v); } return 0; } /* 8 9 1 2 1 3 1 4 2 4 3 4 4 5 4 6 6 8 6 7 */
敲熟了 完结
线段树
【脑抽,t1去敲线段树,关键是还敲挂了】
模板
注解的地方是踩雷区。总结一下感觉还是对lazy的理解不深刻
合并区间的时候脑抽一下,lazy其实对于当前节点是执行了加权操作,只是暂时不修改更多的儿子罢了..
#include<bits/stdc++.h> using namespace std; #define N 100100 #define ll long long #define lc (p<<1) #define rc (p<<1|1) #define mid (t[p].l+t[p].r>>1) ll n,m,op; ll a[N]; struct email { ll l,r,sum,lazy; }t[N*4]; inline void pushnow(ll p,ll v) { t[p].sum+=(t[p].r-t[p].l+1)*v; t[p].lazy+=v; } inline void pushdown(ll p) { if(t[p].lazy) { pushnow(lc,t[p].lazy); pushnow(rc,t[p].lazy); t[p].lazy=0; } } inline void pushup(ll p) { t[p].sum=t[lc].sum+t[rc].sum; //t[p].sum+=t[lc].sum+t[rc].sum; } inline void build(ll p,ll l,ll r) { t[p].l=l;t[p].r=r; if(l==r) { t[p].sum=a[l]; return ; } ll bmid=l+r>>1; build(lc,l,bmid);build(rc,bmid+1,r); pushup(p); } inline void update(ll p,ll ql,ll qr,ll v) { if(ql<=t[p].l&&qr>=t[p].r) { pushnow(p,v);//t[p].sum+=v;t[p].lazy+=v; return ; } pushdown(p); if(ql<=mid)update(lc,ql,qr,v); if(qr>mid)update(rc,ql,qr,v); pushup(p); } inline ll query(ll p,ll ql,ll qr) { ll ret=0; if(ql<=t[p].l&&qr>=t[p].r) return t[p].sum; pushdown(p); if(ql<=mid) ret+=query(lc,ql,qr); if(qr>mid) ret+=query(rc,ql,qr); return ret; } int main() { scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,1,n); for(ll i=1;i<=m;i++) { scanf("%lld",&op); if(op==1) { ll x,y,v; scanf("%lld%lld%lld",&x,&y,&v); update(1,x,y,v); } if(op==2) { ll x,y; scanf("%lld%lld",&x,&y); printf("%lld ",query(1,x,y)); } } return 0; }
例题
结束
树状数组
【敲挂了线段树过后就敲了树状数组,又挂了。。】
模板
区间修改单点查询(区间查询的我选择用线段树...怕加加减减混了影响心态)
#include<bits/stdc++.h> using namespace std; #define N 100100 int n,m,op; int a[N],b[N]; inline int lowbit(int x) { return x&(-x); } inline void add(int x,int y) { for(;x<=n;x+=lowbit(x)) b[x]+=y; } inline int query(int x) { int ret=0; for(;x;x-=lowbit(x)) ret+=b[x]; return ret; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) { scanf("%d",&op); if(op==1) { int l,r,v; scanf("%d%d%d",&l,&r,&v); add(l,v);add(r+1,-v); } if(op==2) { int k; scanf("%d",&k); printf("%d ",query(k)+a[k]); } } return 0; }
例题,逆序对
结束
树的直径
【花花森林虽然虚惊一场,但是树的直径确实只是知道思想,还是得去敲一敲】
dp求直径模板
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100010 int n,m,cnt,ans; int d[N],vis[N],first[N]; struct email { int u,v,w; int nxt; }e[N*4]; inline void add(int u,int v,int w) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v;e[cnt].w=w; } void dp(int u) { vis[u]=1; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v,w=e[i].w; if(vis[v])continue; dp(v); ans=max(ans,d[v]+d[u]+w); d[u]=max(d[u],d[v]+w); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v,w; char dir[3]; scanf("%d%d%d%s",&u,&v,&w,dir); add(u,v,w);add(v,u,w); } dp(1); printf("%d ",ans); return 0; }
两次dfs
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100010 int n,m,cnt,ans1,ans2; int d[N],vis[N],first[N]; struct email { int u,v,w; int nxt; }e[N*4]; inline void add(int u,int v,int w) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v;e[cnt].w=w; } void dfs(int u) { vis[u]=1; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v,w=e[i].w; if(vis[v])continue; d[v]=d[u]+w; dfs(v); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v,w; char dir[3]; scanf("%d%d%d%s",&u,&v,&w,dir); add(u,v,w);add(v,u,w); } dfs(1); for(int i=1;i<=n;i++) if(ans1<d[i]) ans1=d[i],ans2=i; memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); dfs(ans2); for(int i=1;i<=n;i++) ans1=max(ans1,d[i]); printf("%d ",ans1); return 0; }
逆元
【到现在也不是很懂,花花森林的逆元为什么不能用费马小定理啊..还是我写挂了的原因??】
终于填上此坑了!!
四种方法求逆元
值得拥有!!!证明都很简单,手玩一下吧。玩不出来就背呗。