您的位置:首页 > 其它

树上倍增

2017-02-10 09:24 323 查看
寒假集训第一天和最后一天分别考了这两道题,这两道题做法几乎完全一样,可谓首尾呼应,然而我还是没做对(捂脸),果然还是编程能力有问题。





两道题做法均为先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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: