一道人生哲理题(一步一步优化)
https://vjudge.net/contest/500589#problem/D
一开始想拓扑,用了一个multiset维护其子节点对自己的贡献,然后每次将他的set遍历一遍,然后就,再决定他用不用加一下。
因为贪心所以尽量加到r[x],因为贪心,把当前点加到l[x],即可,因为他对父亲的贡献不是有自己决定的,有父亲节点决定。
但是TLE,分析,扫multiset的过程其实没必要,这样就是恰好满足r[x],但是用范围就行,
还是:决定父节点的不在自己,而在于其父亲。
所以只用知道儿子贡献总和的上限就行了,若大于l[x],不用处理,若小于r[x],贪心来讲加满更优,父节点的上界更大。还得向上传,因为每次只分析两层的状态,将状态向上传。
超时代码
点击查看代码
#include<cstdio>
#include<algorithm>
#include<set>
#include<queue>
using namespace std;
const int N=2e5+10;
typedef long long ll;
int cnt[N],p[N],n;
ll a[N],l[N],r[N],ans;
queue<int> q;
struct cmp{
bool operator()(const ll& a,const ll& b){return a>b;}
};
multiset<ll,cmp> bar[N];
//表示子节点自己积累的
void init(){
ans=0;
fill(cnt+1,cnt+1+n,0);
fill(a+1,a+1+n,0);
for(int i=1;i<=n;++i)if(!bar[i].empty())bar[i].clear();
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
init();
for(int i=2;i<=n;++i)scanf("%d",&p[i]),cnt[p[i]]++;
for(int i=1;i<=n;++i)scanf("%lld%lld",&l[i],&r[i]);
for(int i=1;i<=n;++i)if(!cnt[i])q.push(i),bar[i].insert(r[i]),ans++;
while(!q.empty()){
int x=q.front();q.pop();
int f=p[x];cnt[f]--;
if(!cnt[f])q.push(f);
//只要小于r[x]就可以向上传,才会更大。若等于或大于没必要传。
//若子节点的都传完了,那么就需要自己加了,这时答案就需要修改
if(a[x]<r[x]){
while(a[x]<r[x]&&!bar[x].empty()){
ll maxn=*bar[x].begin();bar[x].erase(bar[x].begin());
ll Cha=r[x]-a[x];
if(Cha<=maxn){
a[x]=r[x];
bar[f].insert(Cha);
}else{
a[x]+=maxn;
bar[f].insert(maxn);
}
}
if(a[x]<l[x]){
ans++;
bar[f].insert(r[x]-a[x]);
a[x]=r[x];
}
}
}
printf("%lld\n",ans);
}
return 0;
}
AC代码
点击查看代码
#include<cstdio>
#include<algorithm>
#include<set>
#include<queue>
using namespace std;
const int N=2e5+10;
typedef long long ll;
int cnt[N],p[N],n;
ll a[N],l[N],r[N],ans;
queue<int> q;
void init(){
ans=0;
fill(cnt+1,cnt+1+n,0);
fill(a+1,a+1+n,0);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
init();
for(int i=2;i<=n;++i)scanf("%d",&p[i]),cnt[p[i]]++;
for(int i=1;i<=n;++i)scanf("%lld%lld",&l[i],&r[i]);
for(int i=1;i<=n;++i)if(!cnt[i])q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
int f=p[x];cnt[f]--;
if(!cnt[f])q.push(f);
if(a[x]<l[x])ans++,a[x]=r[x];
if(a[x]>r[x])a[x]=r[x];
a[f]+=a[x];
}
printf("%lld\n",ans);
}
//只要小于r[x]就可以向上传,才会更大。若等于或大于没必要传。
//若子节点的都传完了,那么就需要自己加了,这时答案就需要修改
//害。。。用什么multiset啊,用一个数组表示表示一下不就得了。
return 0;
}