树上倍增

来源:互联网 发布:快餐餐饮软件 编辑:程序博客网 时间:2024/05/02 23:40

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


题目一


题目二


两道题做法均为先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(const 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>#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;}
0 0
原创粉丝点击