bzoj 5072 [Lydsy1710月赛]小A的树——树形dp

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5072

发现对于每个子树,黑点个数确定时,连通块的大小取值范围一定是一段区间;所以考虑只最小化最小值、最大化最大值,记 f 和 g 简单dp即可。

注意可能从当前子树里选0个点!此时会用自己更新自己!!所以要先复制一份原来的用来更新!

快速回答询问,本可以记差分数组,每个子树算完后给合法部分区间赋值;但空间开不下。

于是绞尽脑汁,终于想出可以开 bool 数组分块来赋值!!然而WA得不行。

交流后发现那个“取值是一段区间”的性质,在全局也是适用的(很明显……)!所以只要更新一下合法的最值就行了……

然后对拍半天 bool 分块,发现因为有 0 的值,所以标号应该是 0~base-1 这样;而且代码里注释的那个部分是 < 和 >= 而不是 <= 和 >!

然后又因为数据生成出错而以为自己还是不对而又拍、查了半天;最后分块的方法虽然慢一点,但也A了。感觉很好!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=5002,M=73;
int T,n,q,base,hd[N],xnt,to[N<<1],nxt[N<<1],siz[N],f[N][N],g[N][N],tf[N],tg[N];
bool b[N],ok[N][N],ok2[M][N];//siz,black
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
  while(ch>=‘0‘&&ch<=‘9‘) ret=(ret<<3)+(ret<<1)+ch-‘0‘,ch=getchar();
  return fx?ret:-ret;
}
void add(int x,int y)
{
  to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;
  to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;
}
int calc(int x){return x/base+1;}//0~base-1
void dfs(int cr,int fa)
{
  f[cr][b[cr]]=g[cr][b[cr]]=1; siz[cr]=b[cr];
  for(int i=hd[cr],v;i;i=nxt[i])
    if((v=to[i])!=fa)
      {
    dfs(v,cr);
    memcpy(tf,f[cr],sizeof f[cr]);
    memcpy(tg,g[cr],sizeof g[cr]);
    for(int j=siz[cr]+siz[v];j>=b[cr];j--)
      for(int k=max((int)b[v],j-siz[cr]);k<=siz[v]&&k<=j;k++)
        {
          f[cr][j]=min(f[cr][j],tf[j-k]+f[v][k]);
          g[cr][j]=max(g[cr][j],tg[j-k]+g[v][k]);
        }
    siz[cr]+=siz[v];
      }
  /*
  for(int i=0;i<=siz[cr];i++)
    f[0][i]=min(f[0][i],f[cr][i]),
      g[0][i]=max(g[0][i],g[cr][i]);
  */
  for(int i=0;i<=siz[cr];i++)
    {
      if(f[cr][i]>g[cr][i])continue;
      int l=calc(f[cr][i]),r=calc(g[cr][i]);
      if(r-l<=1)
    {
      for(int j=f[cr][i];j<=g[cr][i];j++)
        ok[j][i]=1;
    }
      else
    {
      for(int j=l+1;j<r;j++)ok2[j][i]=1;
      int lm=l*base;
      for(int j=f[cr][i];j<lm;j++)ok[j][i]=1;//<
      lm=(r-1)*base;
      for(int j=g[cr][i];j>=lm;j--)ok[j][i]=1;//>=
    }
    }
}
int main()
{
  T=rdn();
  while(T--)
    {
      n=rdn(); q=rdn(); base=sqrt(n);
      memset(hd,0,sizeof hd); xnt=0;
      memset(ok,0,sizeof ok); memset(ok2,0,sizeof ok2);
      for(int i=1,u,v;i<n;i++)
    {
      u=rdn(); v=rdn(); add(u,v);
    }
      for(int i=1;i<=n;i++)b[i]=rdn();
      memset(f,0x3f,sizeof f); memset(g,-2,sizeof g);
      dfs(1,0);
      for(int i=1,x,y,d;i<=q;i++)
    {
      x=rdn(); y=rdn(); d=calc(x);
      puts((ok[x][y]||ok2[d][y])?"YES":"NO");
      //puts(x>=f[0][y]&&x<=g[0][y]?"YES":"NO");
    }
      puts("");
    }
  return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/9819243.html

时间: 2024-11-09 21:10:13

bzoj 5072 [Lydsy1710月赛]小A的树——树形dp的相关文章

【bzoj5072】[Lydsy十月月赛]小A的树 树形背包dp

题解: 比较好想 首先注意到如果最暴力的做法复杂度无法接受 而5000的范围基本是n^2做法了 只使用已经遍历过的点数目和当前子树中的点数目转移我们知道复杂度是n^2的 于是大胆猜测一波同一个节点为根值域是连续的 然后就可以暴力dp了 查询的时候我们可以把序列差分一下(原问题等价于 区间修改,区间询问) 其实更强一点的结论是 对于整棵树,值域都是连续的 但我感觉这个我并不会证(网上代码好像基本都是用这个结论的) 代码: 原文地址:https://www.cnblogs.com/yinwuxiao

【BZOJ2616】SPOJ PERIODNI 笛卡尔树+树形DP

[BZOJ2616]SPOJ PERIODNI Description Input 第1行包括两个正整数N,K,表示了棋盘的列数和放的车数. 第2行包含N个正整数,表示了棋盘每列的高度. Output 包括一个非负整数,表示有多少种放置的方案,输出答案mod 1000000007后的结果即可. Sample Input 5 2 2 3 1 2 4 Sample Output 43 HINT 对于100%的数据,有 N≤500,K≤500,h[i] ≤1000000. 题解:一看题就感觉应该是单调

【BZOJ-2286】消耗战 虚树 + 树形DP

2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁

【BZOJ-3572】世界树 虚树 + 树形DP

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种

BZOJ 2286 消耗战 (虚树+树形DP)

给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<=500000) 考虑树形DP,我们令mn[i]表示i节点无法与1节点相连切除的最小权值.显然有mn[i]=min(E(fa,i),mn[fa]).大致就是i到1的简单路径上的最小边.我们对于每个询问.把询问的点不妨称为关键点.令dp[i]表示i节点不能与子树的关键点连接切掉的最小权值.那么有,如果son[i]

BZOJ 1040:[ZJOI2008]骑士(环套外向树+树形DP)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1040 [题目大意] 给出环套外向树森林,求最大权独立集. [题解] 我们对于每个连通块,找到环上的一条边拆开,对于边的两端分别做树形DP, 假设两端点位x和y,那么不包含x的dp值涵盖了是否包含y两种情况, 同理,以y为根的也是,因为边的两端不能同时取到,因此对于两者取最大值即可. 代码中f[x]表示包含x的dp值,g[x]表示不包含x的dp值. [代码] #include <cst

【基环树/树形DP】BZOJ1040-[ZJOI2008]骑士

[题目大意] 有n个骑士,给出他们的能力值和最痛恨的一位骑士.选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力,求战斗力的最大值. [思路] 首先yy一下,可以知道这是一个基环森林.我们可以用以下方法: 首先在每一棵基环树的环上任意找到一条边(用dfs来实现),记它的两个端点为u和v.然后删掉这条边(我这里用的方法是记录u,v在对方容器中的位置,并在后续操作中忽略这条边).由于u和v不能同时取,在删掉u和v之间

bzoj 4424: Cf19E Fairy &amp;&amp; codeforces 19E. Fairy【树形dp】

参考:https://blog.csdn.net/heheda_is_an_oier/article/details/51131641 这个找奇偶环的dp1真是巧妙,感觉像tarjan一样 首先分情况讨论,如果没有奇环,每条边都可以删:如果有一个奇环,奇环上隋边山:否则,删被所有奇环覆盖且没被任何一个偶环覆盖的边 那么重点就是怎样找到所有的奇环和偶环 用树形dp来搞,设f[i]记录经过第i条边的奇环数,g[i]记录经过第i条边的偶环数,因为是边的编号而存的是双向边,所以dp的时候用i>>1表示

美团2017年CodeM大赛-初赛B轮 黑白树 (树形dp)

大意: 给定树, 初始每个点全为白色, 点$i$有权值$k_i$, 表示选择$i$后, 所有距离$i$小于$k_i$的祖先(包括i)会变为黑色, 求最少选多少个点能使所有点变为黑色. 链上情况的话, 直接从链头开始做一次线性dp就行了, 但是显然不能拓展到树上情况. 正解是从叶子往上贪心划分, 若当前点$x$为白色, 则从$x$子树内选择一个$y$, 满足$k[y]-dis(x,y)$最大, 这个显然可以用树形dp在O(n)时间求出. #include <iostream> #include