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