zoukankan      html  css  js  c++  java
  • bzoj 5072 小A的树 —— 树形DP

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

    由于对于一个子树,固定有 j 个黑点,连通块大小是一个连续的范围;

    所以记 f[i][j] 表示以 i 为根的子树中选 j 个黑点,连通块最大的点数,g[i][j] 表示最小的点数;

    然后普通树形DP即可,注意初始化;

    但怎么处理询问?这道题卡空间,只能开 2.5 个 5000*5000 的 int 数组;

    其实,对于整棵树,固定有 j 个黑点的连通块大小也是一个连续的范围,所以每个子树的答案直接贡献到 0 的答案上,就可以 O(1) 判断了。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int const xn=5005,inf=0x3f3f3f3f,base=75;
    int T,n,q,f[xn][xn],g[xn][xn],hd[xn],ct,nxt[xn<<1],to[xn<<1],siz[xn],tf[xn],tg[xn];
    bool b[xn];
    void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    void dfs(int x,int fa)
    {
      siz[x]=b[x];
      if(b[x])f[x][1]=g[x][1]=1;//
      else f[x][0]=1,g[x][0]=1;//
      for(int i=hd[x],u;i;i=nxt[i])
        {
          if((u=to[i])==fa)continue;
          dfs(u,x); 
          memcpy(tf,f[x],sizeof f[x]);
          memcpy(tg,g[x],sizeof g[x]);
          for(int j=siz[x]+siz[u];j>=b[x];j--)
        for(int k=0;k<=min(j,siz[u]);k++)
          if(j-k<=siz[x])
            {
              f[x][j]=max(f[x][j],tf[j-k]+f[u][k]);
              g[x][j]=min(g[x][j],tg[j-k]+g[u][k]);
            }
          siz[x]+=siz[u];
        }
      if(!b[x])g[x][0]=0;
      else f[x][0]=g[x][0]=0;
      for(int j=0;j<=siz[x];j++)f[0][j]=max(f[0][j],f[x][j]),g[0][j]=min(g[0][j],g[x][j]);
    }
    int main()
    {
      T=rd();
      while(T--)
        {
          n=rd(); q=rd(); ct=0;
          memset(hd,0,sizeof hd);
          memset(f,-2,sizeof f); memset(g,0x3f,sizeof g);
          for(int i=1,x,y;i<n;i++)
        {
          x=rd(); y=rd();
          add(x,y); add(y,x);
        }
          for(int i=1;i<=n;i++)b[i]=rd();
          dfs(1,0);
          for(int i=1,x,y;i<=q;i++)
        {
          x=rd(); y=rd();
          if(x>=g[0][y]&&x<=f[0][y])puts("YES");
          else puts("NO");
        }
          puts("");
        }
      return 0;
    }
  • 相关阅读:
    Kostya Keygen#2分析
    一个简单的windows勒索软件分析
    MSRHook与SSDTHook
    VS2013中调驱动
    VMProtect1.63分析
    Linux内核分析总结
    进程的切换和系统的一般执行过程
    可执行程序的装载
    进程的描述和进程的创建
    扒开系统调用的三层皮(下)
  • 原文地址:https://www.cnblogs.com/Zinn/p/9818902.html
Copyright © 2011-2022 走看看