51nod 1307

题意:给出一棵树,每条边上都有一个权值,每个点也有一个权值,当一条边的子树所有节点权值和超过这条边的权值时,这条边会断开。边按序给出,问最多能有几条边。
每条边的可承受重量可以看成其下端点的可承受重量。
数据比较弱,首先可以按题意模拟,当加入一个新点时,向上不断查找父节点,查看新点到根节点的这条路径是否有边会超限断开。

#include<cstdio>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int maxn=5e4+50;
const int maxe=5e4+50;
const LL Inf=1e18;
int r[maxn], w[maxn], p[maxn];
int n;
int main(){

    int par;
    int ans=-1;
    scanf("%d", &n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d", &r[i], &w[i], &p[i]);
        p[i]++;
        if(ans>=0) continue;
        int v=i, tw=w[i];
        while(v){
            r[v]-=tw;
            if(r[v]<0) ans=i-1;
            v=p[v];
        }
    }
    if(ans==-1) ans=n;
    printf("%d\n", ans);
    return 0;
}

但上面那种方法的复杂度是O(n^2)的,在整棵树退化成一条链的时候,n=50000时耗时6s+。
最大值问题,考虑二分,每次二分出来的值可以通过一次深搜来验证。

#include<cstdio>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int maxn=5e4+50;
const int maxe=5e4+50;
const LL Inf=1e18;
struct Edge{
    int from, to, next;
};
Edge edges[maxe];
int head[maxn], ecnt;
int n;
LL r[maxn], w1[maxn], w2[maxn];
bool flag;
void add_edge(int u,int v){
    edges[ecnt]=Edge{u, v, head[u]};
    head[u]=ecnt++;
}
void dfs(int u,int lim){
    if(!flag) return;
    LL &tmp=w2[u];
    tmp=w1[u];
    for(int i=head[u];i!=-1;i=edges[i].next)
    if(i<=lim){
        int v=edges[i].to;
        dfs(v, lim); if(!flag) return;
        tmp+=w2[v];
    }
    if(tmp>r[u]) flag=false;
}
void init(){
    int par;
    memset(head, -1, sizeof head);
    ecnt=1;
    scanf("%d", &n);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld%d", &r[i], &w1[i], &par);
        par++;
        add_edge(par, i);
    }
    w1[0]=0;
    r[0]=Inf;
}
void solve(){
    int l=1, r=n;
    int ans=0;
    while(l<=r){
        int mid=(l+r)>>1;
        flag=true;
        dfs(0, mid);
        if(flag){
            ans=mid;
            l=mid+1;
        }else{
            r=mid-1;
        }
    }
    printf("%d\n", ans);
}
int main(){

    init();
    solve();
    return 0;
}

但上面的代码在树深度过深时,比如n=50000且退化成一条链时,会爆栈。把递归深搜改成循环,把使用系统栈改成使用STL的栈。

#pragma comment (linker,"/stack:102400000,102400000")
#include<stack>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int maxn=5e4+50;
const int maxe=5e4+50;
const LL Inf=1e18;
struct Edge{
    int from, to, next;
};
Edge edges[maxe];
int head[maxn], ecnt;
int n;
LL r[maxn], w1[maxn], w2[maxn];
bool flag;
stack<int> S;
queue<int> Q;
void add_edge(int u,int v){
    edges[ecnt]=Edge{u, v, head[u]};
    head[u]=ecnt++;
}
void dfs(int x,int lim){
    Q.push(x);
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        S.push(u);
        for(int i=head[u];i!=-1;i=edges[i].next)
        if(i<=lim){
            int v=edges[i].to;
            Q.push(v);
        }
    }
    while(!S.empty()){
        int u=S.top(); S.pop();
        LL &tmp=w2[u];
        tmp=w1[u];
        for(int i=head[u];i!=-1;i=edges[i].next)
        if(i<=lim){
            int v=edges[i].to;
            tmp+=w2[v];
            if(tmp>r[u]) flag=false;
            if(!flag) return;
        }
    }
}
void init(){
    int par;
    memset(head, -1, sizeof head);
    ecnt=1;
    scanf("%d", &n);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld%d", &r[i], &w1[i], &par);
        par++;
        add_edge(par, i);
    }
    w1[0]=0;
    r[0]=Inf;
}
void solve(){
    //puts("Infinity is awesome!");
    int l=1, r=n;
    int ans=0;
    while(l<=r){
        int mid=(l+r)>>1;
        flag=true;
        dfs(0, mid);
        if(flag){
            ans=mid;
            l=mid+1;
        }else{
            r=mid-1;
        }
    }
    printf("%d\n", ans);
}
int main(){

    //freopen("data.txt", "r", stdin);
    init();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值