loj3166. 「CEOI2019」魔法树
题意:自已去看
题解:首先考虑dp。设\(dp_{i,j}\)表示\(i\)的子树内,总时间为\(j\)时的最大收益。转移是显然的,能对其造成贡献的是每一个儿子\(v\)的\(dp_{v,k}(k \leq j)\)。然后再加上自己的贡献即可。
优化1:发现有用的时间只有\(n\)种,所以可以将时间离散化。时间复杂度是\(O(n^2)\)的。
优化2:我们先不考虑当前节点的贡献。发现两个儿子直接合并的复杂度是\(O(n)\)的,考虑优化。发现对于每个\(i\)和其子树内出现过的\(j\),\(dp_{i,j}\)一定是递增的,因为\(dp_{i,j}=max(dp_{i,j},dp_{i,j-1})\)。所以如果我们用map记录\(dp\)数组,并将\(dp\)数组差分,那么就能做到启发式合并了。直接合并两个map即可。
最后考虑当前节点的果实的贡献。设当前节点果实成熟的时间为\(t\),那么\(dp_{i,t}+=w_i\),然后考虑有一些\(dp_{i,j}(j>t)\)会比\(dp_i,t\)小,那么找到这些小的,把它们从map里删除就行了。
时间复杂度:\(O(nlog^2n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
vector<int>e[101000],vec[101000];
map<int,ll>s[101000];
int n,m,k,fa[101000],root[101000],t[101000];
ll w[101000];
ll ans,sum;
inline bool bbb(int x,int y){return t[x]==t[y]?w[x]<w[y]:t[x]<t[y];}
I D_1(int x){
for(auto d:e[x]){
D_1(d);
if(s[x].size()<s[d].size())swap(s[x],s[d]);
for(auto it=s[d].begin();it!=s[d].end();it++){
s[x][it->first]+=it->second;
}
s[d].clear();
}
sort(vec[x].begin(),vec[x].end(),bbb);
for(auto d:vec[x]){
s[x][t[d]]+=w[d];register ll dt=w[d];
for(auto it=s[x].upper_bound(t[d]),tmp=it;it!=s[x].end();){
if(dt>=it->second){dt-=it->second;tmp=it++,s[x].erase(tmp);}
else{it->second-=dt;break;}
}
}
}
int main(){
read(n);read(m);read(k);
F(i,2,n)read(fa[i]),e[fa[i]].emplace_back(i);
re X;
F(i,1,m){
read(X);vec[X].emplace_back(i);
read(t[i]);read(w[i]);
}
D_1(1);
for(auto it=s[1].begin();it!=s[1].end();it++){
sum+=it->second;
}
printf("%lld",sum);
return 0;
}
/*
6 4 10
1
2
1
4
4
3 4 5
4 7 2
5 4 1
6 9 3
*/