首页 > 其他分享 >P3267 JLOI2016/SHOI2016 侦察守卫

P3267 JLOI2016/SHOI2016 侦察守卫

时间:2024-06-11 13:55:01浏览次数:13  
标签:JLOI2016 P3267 int tot maxn SHOI2016 转移

P3267 JLOI2016/SHOI2016 侦察守卫

互相赋值的双 dp

思路

设 \(f[u][i]\) 表示包括 \(u\) 子树内所有关键点都被覆盖(包括 \(u\)),且至少还可以向 \(u\) 的父亲方向覆盖 \(i\) 层的最小代价。

设 \(g[u][i]\) 表示向下距离大于等于 \(i\) 的点全部被覆盖,剩下距离小于 \(i\) 的点随意的最小代价。

设 \(v\) 是 \(u\) 的儿子,有转移:

\[f[u][i]=\min(f[u][i]+g[v][i],g[u][i+1]+f[v][i+1])\\ g[u][i]=\sum_{v\in u.sons} g[v][i-1] \]

\(f\) 的转移可以理解为自己覆盖,和儿子覆盖两种情况。

根据 \(f\) 和 \(g\) 的定义还有以下转移:

\[f[u][i]=\min(f[u][i],f[u][i+1])\\ g[u][i]=\min(g[u][i],g[u][i-1]) \]

有了方程之后还需要区分关键点和普通点的初始值。

若为关键点 \(f[u][0]=w[u]\)。

若为普通点 \(f[u][0]=0\)。

这里初始值是只包括自己一个节点,需要和儿子进行合并得到最终答案。

又 \(f[u][i]=w[u]\),满足 \(1\leq i\leq d\)。

当然了,我们在转移时先转移 \(f\) 再转移 \(g\),因为 \(f\) 转移时 \(g\) 的定义是不包括当前的儿子的。

对于 \(g[u][0]\) 每次需要附初值 \(g[u][0]=f[u][0]\)。

转移顺序详见代码。

CODE

#include<bits/stdc++.h>
using namespace std;

const int maxn=5e5+5;

struct Edge
{
    int tot;
    int head[maxn];
    struct edgenode{int to,nxt;}edge[maxn*2];
    inline void add(int x,int y)
    {
        tot++;
        edge[tot].to=y;
        edge[tot].nxt=head[x];
        head[x]=tot;
    }
}T;

int n,d,m;
int f[maxn][50],g[maxn][50],w[maxn];

bool vis[maxn];

inline void dfs(int u,int fa)
{
    if(vis[u]) g[u][0]=f[u][0]=w[u];
    else g[u][0]=f[u][0]=0;
    for(int i=1;i<=d;i++) f[u][i]=w[u];
    for(int i=T.head[u];i;i=T.edge[i].nxt)
    {
        int v=T.edge[i].to;
        if(v==fa) continue;
        dfs(v,u);
    }
    for(int i=T.head[u];i;i=T.edge[i].nxt)
    {
        int v=T.edge[i].to;
        if(v==fa) continue;
        for(int j=d;~j;j--) f[u][j]=min(min(f[u][j]+g[v][j],g[u][j+1]+f[v][j+1]),f[u][j]+f[v][j+1]);
        for(int j=d;~j;j--) f[u][j]=min(f[u][j+1],f[u][j]);
        g[u][0]=f[u][0];
        for(int j=1;j<=d+1;j++) g[u][j]+=g[v][j-1];
        for(int j=1;j<=d+1;j++) g[u][j]=min(g[u][j],g[u][j-1]);
    }
    for(int j=d;~j;j--) f[u][j]=min(f[u][j+1],f[u][j]);
    for(int j=1;j<=d+1;j++) g[u][j]=min(g[u][j],g[u][j-1]);
}

int main()
{
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    scanf("%d",&m);
    for(int i=1,x;i<=m;i++) scanf("%d",&x),vis[x]=true;
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        T.add(x,y),T.add(y,x);
    }
    memset(f,0x3f,sizeof(f));
    dfs(1,0);
    int ans=1e9;
    for(int i=0;i<=d;i++)
        ans=min(ans,f[1][i]);
    printf("%d",ans);
}

标签:JLOI2016,P3267,int,tot,maxn,SHOI2016,转移
From: https://www.cnblogs.com/binbinbjl/p/18241921

相关文章

  • P3270 [JLOI2016] 成绩比较
    记\(a_i=N-R_i,n=N-1\)。先不考虑有多少人被碾压,计算出符合排名限制的方案数。枚举每门课和B神在每门课中的得分,选出\(a_i\)个人得分小于等于他,即:\[\prod\limits_{i=1}^m\dbinom{n}{a_i}\sum\limits_{j=1}^{U_i}j^{a_i}(U_i-j)^{n-a_i}\]设\(s(x)=\sum\limits_{j=1}^{......
  • 【题解】JLOI2016 - 成绩比较
    【题解】JLOI2016-成绩比较https://loj.ac/p/2026是我会的题,所以感觉难度不如noipT3T4。设\(f_{i,j}\)表示考虑到前\(i\)门课,有\(j\)人被B碾压。转移,设这轮中有\(k\)个原本被碾压的人不再被碾压,则相当于从\(f_{i-1,j+k}\)转移到\(f_{i,j}\)。考虑转移系数,首......
  • [JLOI2016]成绩比较
    题目描述G系共有\(N\)位同学,\(M\)门必修课。这\(N\)位同学的编号为\(0\)到\(N-1\)的整数,其中B神的编号为\(0\)号。这\(M\)门必修课编号为\(0\)到\(M-1\)的整数。一位同学在必修课上可以获得的分数是\(1\)到\(U_i\)中的一个整数。如果在每门课上A获得......
  • [JLOI2016]成绩比较
    首先我们让恰有\(k\)位同学被碾压是比较困难的,我们套路地把它转换成钦定某\(k\)位同学被碾压。考虑到分数的分配方案数只与多少个人比B大/多少个人小于等于B相关,而这部分是个定值,所以我们接下来只需要对每门课把所有人分成两个集合就可以了。我们记钦定某\(k\)位同学......
  • 【容斥、插值】P3270 [JLOI2016]成绩比较
    【容斥、插值】P3270[JLOI2016]成绩比较题目简述有\(n+1\)个人,进行\(m\)场考试,第\(i\)场考试的可能得分是\([0,U_i]\)之间的整数。假设你是其中一人,你知道每......
  • 【题解】洛谷-P3270 [JLOI2016]成绩比较
    式子有点长,步骤有点多,所以写一下。题目要求恰好\(k\)人被吊打的方案数,容易想到二项式反演,设\(f(k)\)为钦定\(k\)人其他任意的方案数,\(g(k)\)为恰好\(k\)人的方案......
  • 【JLOI2016_SHOI2016】侦察守卫(树形DP)
    考虑树形DP,假设我们已经考虑完当前子树内监听点的放置情况,根为\(u\),考虑我们要记录什么状态:\(u\)子树内的监听点向子树外还能监听多远,\(u\)子树内距离根最远的未被监听......