Contest Hunter 模拟赛09 C [树形dp+差分]
题面
思路
又双叒叕是一道差分题我没想出来......记录一下
首先这个“所有祖先都比自己小”等价于“父亲比自己小”
这题的基础dp方程很显然,$dp[u][i]$表示当前在点$u$,且点$u$的值是$i$的时候最小修改几个点
然后我们发现每个$dp[u]$可以取到的有意义的值只有几个,所以我们考虑开一个$set$来维护这些取值和它们对应的$dp$值
这样并不方便转移:我们发现可以把$dp$值差分一下,每个$set$元素维护取值,以及这个取值的$dp$值和上一个取值的$dp$值之间的差——显然这个是单调递增的,否则会不优
然后发现我们可以启发式合并子树的$set$,然后针对当前点$dp$,返回的$dp$值需要保证所有儿子的$set$的最大值都大于这个$dp$值
然后就发现做完了,因为用了启发式合并,总复杂度为$O(n\log ^2 n)$
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,first[200010],cnte=-1;
struct edge{
int to,next;
}a[400010];int w[200010];
bool fl[200010];
multiset<int>s[200010];
inline void add(int u,int v){
a[++cnte]=(edge){v,first[u]};first[u]=cnte;
a[++cnte]=(edge){u,first[v]};first[v]=cnte;
}
void dfs(int u,int f){
int i,v;fl[u]=1;
multiset<int>::iterator it;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;if(v==f) continue;
dfs(v,u);fl[u]=fl[u]&&fl[v];
if(w[u]>w[v]) fl[u]=0;
if(s[u].size()<s[v].size()) s[u].swap(s[v]);
for(auto x:s[v]) s[u].insert(x);
s[v].clear();
}
// cout<<"finish dfs "<<u<<' '<<fl[u]<<'\n';
s[u].insert(1);
it=s[u].upper_bound(w[u]);it--;
s[u].erase(it);
s[u].insert(w[u]+1);
}
int main(){
memset(first,-1,sizeof(first));
n=read();int i,t1,t2;
for(i=1;i<=n;i++) w[i]=read();
for(i=1;i<n;i++){
t1=read();t2=read();
add(t1,t2);
}
dfs(1,0);
int minn=*s[1].begin(),ans=0;
if(fl[1]==1){puts("0");return 0;}
for(auto x:s[1]) if(x==minn) ans++; else break;
cout<<ans<<'\n';
}