Codefoces Gym 101915 G Robots : Brute Force

本文探讨了一道关于树形结构的复杂算法问题,利用离线查询与链剖分(LCT)解决树上路径最大边权的查询。通过预处理与动态更新,实现了高效查询,特别适用于数据规模较大、查询频繁的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

发一个水题。。证明我还活着。。。

题意

给出一棵边权互不相同且根为1的树,并给出q次询问,每次询问给出一个x,问,从根出发,只能往儿子走,每次会选择不超过x的最大的边走下去,如果不存在儿子或者不存在这样的边,则停止,问最终会停在哪个点,将所有询问的答案求和输出。

题解:

无脑一把梭题解 :离线,然后LCT,没了。
暴力题解:离线,从小到大把边插入树中,每次插边的时候,如果近根点已经有了一个儿子边,那么把它删掉,然后把新边插入进去,也就是说存活的边组成了若干不想交的链。这样我们只需要求得每次插入边之后,从根出发的路径延伸到哪里就可以每次log(二分边权)回答询问了。然后想一下。。。每条边被插入一次,删除一次,删除之后,整个子树都没了。然后就暴力维护暴力爬就完事了。。。因为一条边最多被爬到两次:一次是爬下去,一次是整个子树被删掉的时候爬上来。。。这部分是纯线性的。不懂为什么5s。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

#define PB(x) push_back(x)
#define rep(i,l,r) for (ll i = l,_ = r;i< _;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0);


/************* header ******************/
const int maxn = 1e5+100;
int first[maxn],nxt[maxn*2],des[maxn*2],dep[maxn],tot;
int father[maxn];
struct Edge{
    int u,v,len;
};
int son[maxn];
int ans[maxn];
bool used[maxn];
vector<Edge> edges;
int n,q;
bool cmp(const Edge &x,const Edge &y){
    return x.len < y.len;
}
void clear(){
    edges.clear();
    for (int i=1;i<=n;i++){
        son[i] = 0;
        first[i] = 0;
        used[i] = 0;
        ans[i] = 0;
    }
    tot =0;
}
inline void addEdge(int x,int y,int z){
    tot ++;
    des[tot] = y;
    nxt[tot] = first[x];
    first[x] = tot;
}
void dfs(int node,int fa){
    father[node] = fa;
    dep[node] = dep[fa] +1;
    for (int t = first[node];t;t=nxt[t]){
        int v = des[t];
        if (v == fa)continue;
        dfs(v,node);
    }
}

void solve(){
    scanf("%d%d",&n,&q);
    clear();
    for (int i=1;i<n;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        addEdge(x,y,z);
        addEdge(y,x,z);
        edges.push_back({x,y,z});
    }
    dfs(1,0);
    sort(edges.begin(),edges.end(),cmp);
    int now = 1;
    used[1] = 1;
    for (int i=0;i<edges.size();i++){
        Edge & edge = edges[i];
        int x = edge.u;
        int y = edge.v;
        if (dep[x] > dep[y])swap(x,y);
        if (used[x]){
            while (now != x){
                used[now] = 0;
                now = father[now];
            }
        }
        son[x] = y;
        while (son[now]){

            now = son[now];
            used[now] = 1;
        }
        ans[i] = now;
    }
    ll out = 0;
    while (q--){
        int x;
        scanf("%d",&x);
        int index = lower_bound(edges.begin(),edges.end(),(Edge){0,0,x},cmp) - edges.begin();
        if (index >= edges.size() || edges[index].len >= x){
            index --;
        }
        int temp_ans =0;
        if (index == -1){
            temp_ans =1;
        }else{
            temp_ans = ans[index];
        }
        //cout<<temp_ans<<endl;
        out += temp_ans;
    }
    cout<<out<<endl;
}
int main(){
    int T;
    for (scanf("%d",&T);T;T--){
        solve();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值