• 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(nlog ^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]<<'
    ';
    	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<<'
    ';
    }
    
  • 相关阅读:
    每天一点Linux type命令的用法
    Source Insight 3.5 序列号分享
    每天一点Linux 查看帮助
    解决Eclipse因为插件加载失败而无法启动的问题
    php生成随机字符串
    mysql 获取刚插入行id汇总
    mysql left join,right join,inner join用法分析
    php中mkdir()函数的权限问题
    php生成唯一数值
    PHP函数mysql_affected_rows()与mysql_num_rows()有什么区别?
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/10575024.html
Copyright © 2020-2023  润新知