• [HDU多校]Ridiculous Netizens


    [HDU多校]Ridiculous Netizens

    • 点分治

      • 分成两个部分:对某一点P,连通块经过P或不经过P.
      • 经过P采用树形依赖背包
      • 不经过P的部分递归计算
    • 树型依赖背包

      • v点必须由其父亲u点转移过来
      • 即必须经过P点
      • (dp[v][s*a[v]]+=dp[u][s])
      • 第二维代表连通块的乘积
      • 第一维代表经过该点并且一定经过P点的方案数
      • 所以最后父节点还要加上子节点的方案数
    • 空间优化

      • 第二维不能开这么大
      • 稍稍转变含义,改成还能"装下"多少体积
      • (lfloor frac{m}{s} floor)就可以划分很多个等价的状态
    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn = 2010;
    const int mod = 1e9+7;
    struct edge{
        int to,next;
    }e[maxn<<1];
    ll val[maxn],kuai[maxn];
    ll dp[maxn][maxn];
    int nowN,rt,maxP,head[maxn],tol,cnt,n;
    int Size[maxn];
    bool vis[maxn<<1];
    ll ans;
    int m;
    inline void Mod(ll &a,ll b){
       a=a+b<mod?a+b:a+b-mod;
    }
    void init(int n){
        tol=1;
        for(int i=0;i<=n;++i){
            head[i]=0;
        }
        rt=0;
        cnt=ans=0;
    }
    void add(int u,int v){
        tol++;
        vis[tol]=0;
        e[tol].to=v;
        e[tol].next=head[u];
        head[u]=tol;
    }
    void get_rt(int u,int fa){
       int max_part=0;
       Size[u]=1;
       for(int i=head[u],v;i;i=e[i].next){
          v=e[i].to;
          if(vis[i]||v==fa)continue;
          get_rt(v,u);
          Size[u]+=Size[v];
          max_part=max(max_part,Size[v]);
       }
       max_part=max(nowN-Size[u],max_part);
       if(max_part<maxP){
         maxP=max_part;
         rt=u;
       }
    }
    void dfs(int u,int fa){
       for(int i=1;i<=cnt;++i) dp[u][i]=0;
       ll k=val[u],t;
       for(int i=1,j=1;i<=cnt;++i){
            t=kuai[i]/k;
            if(t==0)break;
            while(kuai[j]>t)++j;
            Mod(dp[u][j],dp[fa][i]);
       }
       for(int i=head[u],v;i;i=e[i].next){
          if(vis[i]) continue;
          v=e[i].to;
          if(v==fa) continue;
          dfs(v,u);
          for(int j=1;j<=cnt;++j) Mod(dp[u][j],dp[v][j]);
       }
    }
    void calc(){
       dp[0][1]=1;
       dfs(rt,0);
       for(int i=1;i<=cnt;++i) Mod(ans,dp[rt][i]);
       for(int i=head[rt],v;i;i=e[i].next){
           if(vis[i])continue;
           v=e[i].to;
           vis[i^1]=1;
           nowN=Size[v];
           maxP=n+5;
           rt=0;
           get_rt(v,0);
           calc();
       }
    }
    int main(){
        int _;
        scanf("%d",&_);
        while(_--){
            scanf("%d%d",&n,&m);
            init(n);
            for(int i=1;i<=n;++i)scanf("%lld",&val[i]);
            for(int i=1;i<=m;i=m/(m/i)+1){
                kuai[++cnt]=m/i;
            }
            for(int i=1,u,v;i<n;++i){
                scanf("%d%d",&u,&v);
                add(u,v);
                add(v,u);
            }
            nowN=n;
            maxP=n+5;
            get_rt(1,1);
            calc();
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    代码量还是集中在点分治,不过套班子即可

    DP还是好理解的.

    不要忘记努力,不要辜负自己 欢迎指正 QQ:1468580561
  • 相关阅读:
    k-近邻算法
    配置nginx 高并发 php
    ubuntu上安装mysql 编译安装
    Webpack探索【1】--- 基础知识
    【JavaScript专题】--- 立即执行函数表达式
    Eslint使用指南
    Maven学习笔记【2】 -- 安装和配置
    Maven学习笔记【1】 -- Maven简介
    Git中如何将代码恢复到之前某个节点
    密码硬编码(Password Management: Hardcoded Password)
  • 原文地址:https://www.cnblogs.com/smallocean/p/11355396.html
Copyright © 2020-2023  润新知