题解 运输 树状数组+乘除分块+离线差分

题解 运输

题目描述

KzFSHS.png

KzizB8.png

具体做法与心路历程

一开始没想到正解,于是开始码 O ( m w ) O(mw) O(mw)的暴力,码暴力的时候发现了正解。

具体做法

考虑将询问离线下来,然后差分到树上,暴力的方法就是在 d f s dfs dfs树的时候用一个数组 a n s [ i ] ans[i] ans[i]表示从根走到当前节点,

经过的边在询问的电量为 i i i时所需要的最少次数。那么在 d f s dfs dfs时从对每个 a n s [ i ] ans[i] ans[i]维护一下即可。

正解即是对于某条边 w w w,我们发现它对 a n s ans ans的答案是可以整除分块的,那么我们用树状数组维护 a n s ans ans,整除分开统计即可。

时间复杂度为 O ( m w l o g w ) O(m\sqrt{w}logw) O(mw logw)

C o d e \mathcal{Code} Code

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年11月04日 星期一 08时30分26秒
*******************************/
#include<cstdio>
#include<algorithm>
#include<vector>
#define int long long 

using namespace std;

struct IO{
    template<typename T>
    IO & operator>>(T&res)
    {
        T q=1;char ch;
        while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
        res=(ch^48);
        while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
        res*=q;
        return *this;
    }
}cin;

struct edge{
    int to,next,w;
    edge(int a=0,int b=0,int c=0):to(a),next(b),w(c){}
};

struct Node{
    int id,q,w;
    Node(int a=0,int b=0,int c=0):id(a),q(b),w(c){}
};

const int maxn=1e5+10;
const int LG=20;
const int LIM=3e4;

int n,m,head[maxn],cnt,s[maxn],prime[maxn],tot,ans[maxn],f[maxn][LG+1],depth[maxn];
bool vis[maxn];
edge e[maxn<<1];
vector<Node>V[maxn];

/*{{{BIT*/

namespace BIT{

    int tr[31000];

    void add(int x,int val)
    {
        while(x<=LIM)
        {
            tr[x]+=val;
            x+=x&-x;
        }
    }

    int sum(int x)
    {
        int res=0;
        while(x)
        {
            res+=tr[x];
            x-=x&-x;
        }
        return res;
    }

};

/*}}}*/

void add(int u,int v,int w)
{
    e[++cnt]=edge(v,head[u],w);
    head[u]=cnt;
}

void pre_dfs(int now)
{
    depth[now]=depth[f[now][0]]+1;
    for(int i=1;i<=LG;i++)
        f[now][i]=f[f[now][i-1]][i-1];
    for(int i=head[now];i;i=e[i].next)
        if(e[i].to!=f[now][0])
        {
            f[e[i].to][0]=now;
            pre_dfs(e[i].to);
        }
}

int LCA(int x,int y)
{
    if(depth[x]<depth[y]) swap(x,y);
    for(int i=LG;i>=0;i--)
        if(depth[f[x][i]]>=depth[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=LG;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}

void update(int w,int q)
{
    w--;
    int d;
    for(int l=1,r;l<=w;l=r+1)
    {
        r=w/(w/l);
        d=w/l;
        BIT::add(l,d*q);
        BIT::add(r+1,d*q*(-1));
    }
}

void dfs(int now)
{
    int w,id,q;
    for(int i=0;i<(int)V[now].size();i++)
    {
        w=V[now][i].w,id=V[now][i].id,q=V[now][i].q;
        ans[id]+=q*BIT::sum(w);
    }
    for(int i=head[now];i;i=e[i].next)
        if(e[i].to!=f[now][0])
        {
            w=e[i].w;
            update(w,1);
            dfs(e[i].to);
            update(w,-1);
        }
}

signed main()
{
    //freopen("delivery.in","r",stdin);
    //freopen("delivery.out","w",stdout);
    cin>>n>>m;
    int u,v,w;
    for(int i=1;i<n;i++)
    {
        cin>>u>>v>>w;
        add(u,v,w);
        add(v,u,w);
    }
    pre_dfs(1);
    for(int i=1;i<=m;i++)
    {
        cin>>u>>v>>w;
        if(u==v){ ans[i]=0; continue; }
        ans[i]=depth[u]+depth[v]-2*depth[LCA(u,v)]+1;
        V[u].push_back(Node(i,1,w));
        V[v].push_back(Node(i,1,w));
        V[LCA(u,v)].push_back(Node(i,-2,w));
    }
    dfs(1);
    for(int i=1;i<=m;i++)
    {
        if(ans[i]==0) ans[i]=1;
        printf("%lld\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值