Codeforces 627D Preorder Test(二分+树形DP)

题意:给出一棵无根树,每个节点有一个权值,现在要让dfs序的前k个结点的最小值最大,求出这个值。

考虑二分答案,把>=答案的点标记为1,<答案的点标记为0,现在的任务时使得dfs序的前k个节点都为1.

考虑树形DP。

用dp[u]表示从节点u开始在子树中进行dfs最多可以经过多少个为1的结点,显然,若某一个子树中节点全为1,那么这个可以加到dp[u]中,此外还可以在不全为1的子树中挑选一个加到dp[u]上。

那么答案就是从标记为1的节点当做根,选两颗不完全子树和所有的完全子树(包括从父亲向上的部分)。

那么如果从父亲向上的部分是不完全子树呢,那等价于从这颗不完全子树上的一个深度最小的点做上面的计算一下。所以不需要考虑从父亲向上的部分是不完全子树这个情况。

时间复杂度O(nlogn).

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <bitset>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
const int N=200005;
//Code begin...

struct Edge{int p, next;}edge[N<<1];
int node[N], head[N], cnt=1, dp[N], date[N], siz[N], tag[N], sum, n, K, ans;
bool flag[N];

void add_edge(int u, int v){edge[cnt].p=v; edge[cnt].next=head[u]; head[u]=cnt++;}
void dfs1(int x, int fa, int val){
    siz[x]=1;
    if (node[x]<val) tag[x]=1, ++sum;
    if (node[x]<val) flag[x]=true;
    for (int i=head[x]; i; i=edge[i].next) {
        int v=edge[i].p;
        if (v==fa) continue;
        dfs1(v,x,val); siz[x]+=siz[v]; tag[x]+=tag[v]; flag[x]|=flag[v];
    }
}
void dfs2(int x, int fa, int val){
    dp[x]=1;
    int f=0, s=0;
    for (int i=head[x]; i; i=edge[i].next) {
        int v=edge[i].p;
        if (v==fa) continue;
        dfs2(v,x,val);
        if (!flag[v]) dp[x]+=siz[v];
        else if (node[v]>=val) {
            if (dp[v]>f) s=f, f=dp[v];
            else if (dp[v]>s) s=dp[v];
        }
    }
    dp[x]+=f;
    if (node[x]>=val) {
        if (tag[x]==sum) ans=max(ans,dp[x]+s+n-siz[x]);
        else ans=max(ans,dp[x]+s);
    }
}
bool check(int x){
    mem(siz,0); mem(dp,0); mem(flag,false); mem(tag,0); sum=ans=0;
    dfs1(1,0,x); dfs2(1,0,x);
    return ans>=K;
}
int main ()
{
    int u, v;
    scanf("%d%d",&n,&K);
    FOR(i,1,n) scanf("%d",node+i), date[i]=node[i];
    FO(i,1,n) scanf("%d%d",&u,&v), add_edge(u,v), add_edge(v,u);
    sort(date+1,date+n+1);
    int l=1, r=n+1, mid;
    while (l<r) {
        mid=(l+r)>>1;
        if (l==mid) break;
        if (check(date[mid])) l=mid;
        else r=mid;
    }
    printf("%d\n",date[l]);
    return 0;
}

时间: 2024-10-09 20:39:37

Codeforces 627D Preorder Test(二分+树形DP)的相关文章

Codeforces 461B Appleman and Tree(树形dp)

题目链接:Codeforces 461B Appleman and Tree 题目大意:一棵树,以0节点为根节点,给定每个节点的父亲节点,以及每个点的颜色(0表示白色,1表示黑色),切断这棵树的k条边,使得树变成k+1个联通分量,保证每个联通分量有且仅有1个黑色节点.问有多少种分割方法. 解题思路:树形dp,dp[i][0]和dp[i][1]分别表示子树一下的分割方法中,i节点所在联通块不存在黑节点和已经存在一个黑节点的方案数. #include <cstdio> #include <c

HDU 3586 Information Disturbing(二分+树形dp)

http://acm.split.hdu.edu.cn/showproblem.php?pid=3586 题意: 给定一个带权无向树,要切断所有叶子节点和1号节点(总根)的联系,每次切断边的费用不能超过上限limit,问在保证总费用<=m下的最小的limit. 思路: 对于上限limit我们可以二分查找.然后就是树形dp,看代码就可以理解的. 1 #include<iostream> 2 #include<algorithm> 3 #include<cstring>

Codeforces 462D Appleman and Tree 树形dp

题目链接:点击打开链接 题意: 给定n个点的树, 0为根,下面n-1行表示每个点的父节点 最后一行n个数 表示每个点的颜色,0为白色,1为黑色. 把树分成若干个联通块使得每个联通块有且仅有一个黑点,问有多少种分法(结果mod1e9+7) 思路: 树形dp,每个点有2个状态,已经归属于某个黑点和未归属于某个黑点. #include <cstdio> #include <vector> #include <iostream> using namespace std; #de

Codeforces 543D Road Improvement(树形DP+乘法逆元)

题目大概说给一棵树,树的边一开始都是损坏的,要修复一些边,修复完后要满足各个点到根的路径上最多只有一条坏的边,现在以各个点为根分别求出修复边的方案数,其结果模1000000007. 不难联想到这题和HDU2196是一种类型的树形DP,因为它们都要分别求各个点的答案.然后解法也不难想: dp0[u]表示只考虑以u结点为根的子树的方案数 dp1[u]表示u结点往上走,倒过来,以它父亲为根那部分的方案数 有了这两部分的结果,对于各个点u的答案就是dp0[u]*(dp1[u]+1).这两部分求法如下,画

Codeforces 919D Substring (拓扑排序+树形dp)

题目:Substring 题意:给你一个有向图, 一共有n个节点 , m条变, 一条路上的价值为这个路上出现过的某个字符最多出现次数, 现求这个最大价值, 如果价值可以无限大就输出-1. 题解:当这个有向图构成一个环的时候就会使得值无限大,所以先用拓扑排序判断一下有没有环,如果有环直接输出-1, 如果没有环就再使用树形dp并记忆化存数,来找到最大值. 代码: 1 #include<cstring> 2 #include<iostream> 3 using namespace std

HDU 3586 Information Disturbing (二分+树形dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3586 给定n个敌方据点,1为司令部,其他点各有一条边相连构成一棵树,每条边都有一个权值cost表示破坏这条边的费用,叶子节点为前线.现要切断前线和司令部的联系,每次切断边的费用不能超过上限limit,问切断所有前线与司令部联系所花费的总费用少于m时的最小limit.1<=n<=1000,1<=m<=10^6 dp[i]表示i节点为root的这个子树所破坏的最少费用,if(cost[i]

bzoj 2097: [Usaco2010 Dec]Exercise 奶牛健美操【二分+树形dp】

二分答案,然后dp判断是否合法 具体方法是设f[u]为u点到其子树中的最长链,每次把所有儿子的f值取出来排序,如果某两条能组合出大于mid的链就断掉f较大的一条 a是全局数组!!所以要先dfs完子树才能填a!! #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1000005; int n,m,h

BZOJ 4753 二分+树形DP

思路: 先二分答案 f[x][j]表示在x的子树里选j个点 f[x][j+k]=max(f[x][j+k],f[x][j]+f[v[i]][k]); 初始化 x!=0 -> f[x][1]=p[x]-s[x]*mid x=0 -> f[x][0]=0 类似4033的那样转移 看似O(n^3)实际O(n^2) 加一个二分 复杂度O(能过) //By SiriusRen #include <cstdio> #include <cstring> #include <al

CodeForces 55D Beautiful numbers (树形DP)

想了半天,总算想出来了.这题刚上来的思路很明显是11维DP..但是明显不可取.. 这题的关键在于只要两个数前面的拥有的数字是一样的,而且此时与其最小公倍数的模是一样的,那么这时候就可以认为对所有的数字取模都是相等的,那么后面的总情况数属于完美数的情况也是相同的. 只要想到这步的话,那么基本思路就出来了,我第一次居然脑残的记录lcm与模2520(2到9的最小公倍数),首先lcm相同并不代表出现的数字相同先不说,仅仅这个最大值就太大了,lcm最大是2520,这样的话就需要开一个20*2520*252