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
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")
templateI 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;
}
vectore[101000],vec[101000];
maps[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]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
*/

你可能感兴趣的:(loj3166. 「CEOI2019」魔法树)