CF #329 D

D题,LCA是很明显的。要注意的是,因为是除法,所以最多可以除x>2的有64次,当大于64时可以直接返回0。而且注意到可能会有很多值为1的边,可以使用路径压缩,把边为1的边压缩掉,类似于并查集的路径压缩。

之前只压缩到LCA,一直TLE,可以直接压缩到它们的根节点。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
#define LL long long
using namespace std;

const int MAX=200050;

int head[MAX],tot;
struct Edge{
    int u,v,next;
    LL w;
}edge[MAX*2];
LL w[MAX]; int pre[MAX],depth[MAX],edno[MAX],par[MAX][20];
bool vis[MAX];
queue<int>que;

void addedge(int u,int v,LL w){
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot++;
}

int n,m;

void BFS(int u){
    que.push(u);
    depth[u]=1;
    edno[u]=0;
    while(!que.empty()){
        u=que.front();
        vis[u]=true;
        que.pop();
        for(int e=head[u];e!=-1;e=edge[e].next){
            int v=edge[e].v;
            if(vis[v]) continue;
            depth[v]=depth[u]+1;
            par[v][0]=u;        
            if(w[edno[u]]==1){
                pre[v]=pre[u];
                edno[v]=edge[e].w;
            }
            else{
                pre[v]=u;
                edno[v]=edge[e].w;
            }
            que.push(v);
        }
    }
}

void init(){
    int i,j;
    for(j=1;(1<<j)<=n;j++)
        for(i=1;i<=n;i++)
            if(par[i][j-1]!=-1)
                par[i][j]=par[par[i][j-1]][j-1];
}

int LCA(int a,int b)//最近公共祖先
{
    int i,j;
    if(depth[a]<depth[b])swap(a,b);
    for(i=0;(1<<i)<=depth[a];i++);
        i--;
      //使a,b两点的深度相同
    for(j=i;j>=0;j--)
        if(depth[a]-(1<<j)>=depth[b])
          a=par[a][j];
        if(a==b)return a;
      //倍增法,每次向上进深度2^j,找到最近公共祖先的子结点
    for(j=i;j>=0;j--){
        if(par[a][j]!=-1&&par[a][j]!=par[b][j]){
          a=par[a][j];
          b=par[b][j];
        }
    }
    return par[a][0];
}

LL anum[70];
LL bnum[70];


LL query(int a,int b,LL y){
    int ret=(int)(log((double)y)/log(2.0));
    int lca=LCA(a,b);
/// cout<<lca<<" "<<ret<<endl;
    int ac=0,bc=0;
    while(a!=-1&&depth[a]>depth[lca]){
////        cout<<a<<" "<<w[edno[a]]<<endl;
        if(w[edno[a]]>1){
            y/=w[edno[a]];
            if(y==0) return 0;
        }
        int tp=pre[a];
        while(pre[tp]!=-1&&w[edno[tp]]==1){
            tp=pre[tp];
        }
        int rt=tp; tp=a;
        while(pre[tp]!=rt){
            int tmp=pre[tp];
            pre[tp]=rt;
            tp=tmp;
        }
        a=rt;
    }
    LL ty=y;
    while(b!=-1&&depth[b]>depth[lca]){
///     cout<<b<<" "<<edno[b]<<endl;
        if(w[edno[b]]>1){
            ty/=w[edno[b]];
            bnum[bc++]=w[edno[b]];
            if(ty==0) return 0;
        }
        int tp=pre[b];
        while(pre[tp]!=-1&&w[edno[tp]]==1){
            tp=pre[tp];
        }
        int rt=tp; tp=b;
        while(pre[tp]!=rt){
            int tmp=pre[tp];
            pre[tp]=rt;
            tp=tmp;
        }
        b=rt;
    }
/// cout<<ac<<" "<<bc<<endl;
/// cout<<bnum[bc-1]<<endl;
/// for(int i=0;i<ac;i++) y/=anum[i];
    for(int i=bc-1;i>=0;i--) y/=bnum[i];
    return y;
}


int main(){
    int u,v,op,a,b,p,c;
    LL y;
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(pre,-1,sizeof(pre));
        memset(head,-1,sizeof(head));
        memset(depth,0,sizeof(depth));
        memset(vis,false,sizeof(vis));
        memset(w,0,sizeof(w));
        memset(par,-1,sizeof(par));
        memset(edno,0,sizeof(edno));
        tot=0;
        for(int i=1;i<n;i++){
            cin>>u>>v>>w[i];
            addedge(u,v,i);
            addedge(v,u,i);
        }
        BFS(1);
    /// cout<<pre[2]<<" "<<edno[2]<<endl;
        init();
        for(int i=1;i<=m;i++){
            scanf("%d",&op);
            if(op==1){
                cin>>a>>b>>y;
                cout<<query(a,b,y)<<endl;
            }
            else{
                cin>>p>>y;
                w[p]=y;
            }
        }
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/jie-dcai/p/4957764.html