树上倍增
2017-02-10 09:24
323 查看
寒假集训第一天和最后一天分别考了这两道题,这两道题做法几乎完全一样,可谓首尾呼应,然而我还是没做对(捂脸),果然还是编程能力有问题。
两道题做法均为先Kruskal求最大或最小生成树,之后被增求LCA,同时记录最值。
个人认为难点有二:
初始化时要同时记录一个跟anc结构相似的数组记录最值,以便倍增往上跳时快速求出最值
处理森林,需要对每棵BSTdfs初始化
注:当时看到题目上写不保证没有重边和自环还担心要不要特判一下,其实Kruskal完全可以处理带有重边和自环的图
下面是Nav的代码,Truck的话就是把impossible改成-1、cmp改个符号、求最小值即可
所以说我是跟树上倍增有仇吗,今天又考了一道,树上倍增优化动规,然而我把状态转移方程写错了……
状态转移方程:
f[u]=min{f[father]+wi(deep[father]−deep[u]<=ki)
边界条件:
f[1]=0
代码
两道题做法均为先Kruskal求最大或最小生成树,之后被增求LCA,同时记录最值。
个人认为难点有二:
初始化时要同时记录一个跟anc结构相似的数组记录最值,以便倍增往上跳时快速求出最值
处理森林,需要对每棵BSTdfs初始化
注:当时看到题目上写不保证没有重边和自环还担心要不要特判一下,其实Kruskal完全可以处理带有重边和自环的图
下面是Nav的代码,Truck的话就是把impossible改成-1、cmp改个符号、求最小值即可
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) using namespace std; const int MAXN=1e5,MAXM=3e5; void swap(int & a,int & b){int t=a;a=b;b=t;} struct node{int u,v,w;} a[MAXM+1];int acnt; bool cmp(cons 4000 t node & a,const node & b){return a.w<b.w;} int head[MAXN+1],ecnt; struct edge{int next,to,value;} e[MAXM+1]; void add(int x,int y,int z){ecnt++,e[ecnt].to=y,e[ecnt].next=head[x],head[x]=ecnt,e[ecnt].value=z;} int fa[MAXN+1]; int getfa(int x) { if(fa[x]==x) return x; return fa[x]=getfa(fa[x]); } void unionset(int x,int y){fa[getfa(x)]=getfa(y);} int N,M,Q; int anc[MAXN+1][32]; int deep[MAXN+1]; int maxv[MAXN+1][32]; void dfs(int now,int from) { for(int tmp=head[now];tmp;tmp=e[tmp].next) { if(e[tmp].to==from) continue; deep[e[tmp].to]=deep[now]+1; anc[e[tmp].to][0]=now; maxv[e[tmp].to][0]=e[tmp].value; dfs(e[tmp].to,now); } } void ready() { int i,j; for(i=1;(1<<i)<=N;i++) for(j=1;j<=N;j++) { anc[j][i]=anc[anc[j][i-1]][i-1]; maxv[j][i]=max(maxv[j][i-1],maxv[anc[j][i-1]][i-1]); } } int ans; int getlca(int x,int y) { if(deep[x]<deep[y]) swap(x,y); int maxlogn=floor(log(N)/log(2)); int i,j; for(i=maxlogn;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) ans=max(ans,maxv[x][i]),x=anc[x][i]; if(x==y) return x; for(i=maxlogn;i>=0;i--) if(anc[x][i]!=anc[y][i]) { ans=max(ans,max(maxv[x][i],maxv[y][i])); x=anc[x][i];y=anc[y][i]; } ans=max(ans,maxv[x][0]);ans=max(ans,maxv[y][0]); return anc[x][0]; } int main() { freopen("nav.in","r",stdin); freopen("nav.out","w",stdout); int i; cin>>N>>M; for(i=1;i<=M;i++) { acnt++; scanf("%d%d%d",&a[acnt].u,&a[acnt].v,&a[acnt].w); } sort(a+1,a+M+1,cmp); int tot=0; for(i=1;i<=N;i++) fa[i]=i; for(i=1;i<=M;i++) { if(getfa(a[i].u)!=getfa(a[i].v)) { unionset(a[i].u,a[i].v); tot++; add(a[i].u,a[i].v,a[i].w); add(a[i].v,a[i].u,a[i].w); }if(tot>=N-1) break; } for(i=1;i<=N;i++)//对森林中每棵MST进行dfs预处理 if(!anc[i][0]) dfs(getfa(i),getfa(i)); ready(); cin>>Q; for(i=1;i<=Q;i++) { int A,B; scanf("%d%d",&A,&B); if(getfa(A)!=getfa(B))//判断A、B两点是否在同一MST中 { printf("impossible\n"); continue; } ans=0; getlca(A,B); printf("%d\n",ans); } return 0; }
所以说我是跟树上倍增有仇吗,今天又考了一道,树上倍增优化动规,然而我把状态转移方程写错了……
状态转移方程:
f[u]=min{f[father]+wi(deep[father]−deep[u]<=ki)
边界条件:
f[1]=0
代码
#include<iostream& b73d gt; #include<cstdio> #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) using namespace std; typedef long long ll; const int MAXN=1e5,MAXM=1e5,MAXQ=1e5,INF=~0U>>1; int n,m,q; struct E{int to,next;} e[MAXM+1];int ecnt,G[MAXN+1]; void addEdge(int u,int v){e[++ecnt]=(E){v,G[u]};G[u]=ecnt;} struct T{int next,k,w;} tic[MAXN+1];int tcnt,tH[MAXN+1]; void addTic(int v,int k,int w){tic[++tcnt]=(T){tH[v],k,w};tH[v]=tcnt;} int anc[MAXN+1][18],tmin[MAXN+1][18]; int f[MAXN+1]; void dfs(int u,int fa) { int i; anc[u][0]=fa,tmin[u][0]=f[fa]; for(i=1;i<=18;i++) { anc[u][i]=anc[anc[u][i-1]][i-1]; tmin[u][i]=min(tmin[u][i-1],tmin[anc[u][i-1]][i-1]); } f[u]=u==1?0:INF; for(i=tH[u];i;i=tic[i].next) { ll tmp=INF,v=u,k=tic[i].k,p=0; while(k) { if(k&1) { tmp=min(tmp,tmin[v][p]); v=anc[v][p]; }p++,k>>=1; } f[u]=min(f[u],tmp+tic[i].w); } for(i=G[u];i;i=e[i].next) dfs(e[i].to,u); } int main() { freopen("party.in","r",stdin); freopen("party.out","w",stdout); int i; scanf("%d%d",&n,&m); for(i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); addEdge(v,u); } for(i=1;i<=m;i++) { int v,k,w; scanf("%d%d%d",&v,&k,&w); addTic(v,k,w); } dfs(1,1); scanf("%d",&q); while(q--) { int pos; scanf("%d",&pos); printf("%d\n",f[pos]); } return 0; }
相关文章推荐
- 二叉树期权定价模型
- DHCP(动态主机配置协议)工作流程
- kafka producer发送消息 Failed to update metadata after问题
- iOS开发常用国外网站清单
- 一段可以让整个网站变黑白代码
- AppStore审核被拒
- 配置SpringMVC的<init-param>标签时的错误cvc-complex-type.2.4.a
- javascript重点-表达式和运算符
- ThinkPHP之数据删除和执行原生SQL语句
- linux内核中jiffies的回绕问题
- java微信公众号开发入门(1)--开发准备
- 第一节 html文件的结构
- 青青校园
- 一次dns缓存引发的惨案
- linux sed命令详解
- linux设备驱动归纳总结(六):3.中断下半部之工作队列
- MySql解压版没有data目录解决办法
- 将博客搬至CSDN
- [Shell] if、for、while流程语句以及整数字符串判断比较的实例详解
- IT学习教程