Luogu P12680 Brooklyn Round 1 & NNOI Round 1 D - Apples 题解 [ 绿 ] [ LCA ] [ 线性 DP ] [ 前缀和优化 ]
Apples:糖糖题。
容易设计出一个线性 DP:\(dp_i\) 表示吃到第 \(i\) 个苹果后的最大苹果数,然后 \(O(m^2)\) 枚举前一个苹果,计算两个位置间的距离是否在时间差内,在的话就转移即可。
这样是显然不可过的,因此考虑另一个没有用到的限制:树的深度 \(\le s\),而 \(s\le 10^3\)。注意到两点间的距离可以转化为 \(u\to lca,lca \to v\) 的两段,每一段都在一条竖着的树链上,而因为深度的限制,所以每一条竖着的树链长度都 \(\le s\),则两点间的距离一定 \(\le 2s\)。
因此发现时间差在 \(2s\) 以外的都不用判断距离的限制了,而又因为 \(t\) 互不相同,所以要判断的苹果也不超过 \(2s\) 个,其余苹果取一个前缀最大值直接转移即可。时间复杂度 \(O(ms+n\log n)\)。
求 LCA 采用了 DFS 序求 LCA 的科技,做到了 \(O(1)\) 查询的复杂度。
将 DFS 序求 LCA 发扬光大,让欧拉序求 LCA 成为时代的眼泪!
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=100005,M=200005;
int n,m,s;
int h[N],idx,dep[N],st[N][20],lg[N],dfn[N],tot;
struct Edge{
int v,ne;
}e[M];
struct Node{
ll w,t,p;
bool operator<(const Node &a)const{
return t<a.t;
}
}a[N];
ll dp[N],pre[N];
void add(int u,int v)
{
e[++idx]={v,h[u]};
h[u]=idx;
}
void dfs(int u,int fa)
{
dfn[u]=++tot;
st[dfn[u]][0]=fa;
dep[u]=dep[fa]+1;
for(int i=h[u];i;i=e[i].ne)
{
int v=e[i].v;
if(v==fa)continue;
dfs(v,u);
}
}
int getmin(int x,int y)
{
return (dep[x]<dep[y]?x:y);
}
void init()
{
for(int i=1;i<=100000;i++)lg[i]=int(log2(1.0*i));
for(int j=1;j<=19;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
st[i][j]=getmin(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
int getlca(int u,int v)
{
if(u==v)return u;
u=dfn[u],v=dfn[v];
if(u>v)swap(u,v);
++u;
return getmin(st[u][lg[v-u+1]],st[v-(1<<lg[v-u+1])+1][lg[v-u+1]]);
}
int getdis(int u,int v)
{
int lca=getlca(u,v);
return (dep[u]+dep[v]-2*dep[lca]);
}
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>s;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
for(int i=1;i<=m;i++)
cin>>a[i].w>>a[i].t>>a[i].p;
sort(a+1,a+m+1);
dfs(1,0);
init();
memset(dp,-0x3f,sizeof(dp));
memset(pre,-0x3f,sizeof(pre));
dp[0]=pre[0]=0;
a[0]={1,0,0};
for(int i=1;i<=m;i++)
{
for(int j=max(0,i-2*s);j<i;j++)
{
if(getdis(a[i].w,a[j].w)>a[i].t-a[j].t)continue;
dp[i]=max(dp[i],dp[j]+a[i].p);
}
if(i-2*s>=0)dp[i]=max(dp[i],pre[i-2*s]+a[i].p);
pre[i]=max(pre[i-1],dp[i]);
}
cout<<pre[m];
return 0;
}

浙公网安备 33010602011771号