【JZOJ 5400】【NOIP2017提高A组模拟10.7】Repulsed

时间:2022-12-17 14:27:22

Description

小w 心里的火焰就要被熄灭了。
简便起见,假设小w 的内心是一棵n -1 条边,n 个节点的树。
现在你要在每个节点里放一些个灭火器,每个节点可以放任意多个。
接下来每个节点都要被分配给一个至多k 条边远的灭火器,每个灭火器最多能分配给s 个节点。
至少要多少个灭火器才能让小w 彻底死亡呢?

Solution

贪心,我想到的是从深度从大到小做,用线段树来找相应的匹配点,
但实际上,可以用类似Dp一样的方法了做贪心,
设f[i][j]表示第i个点,它往下j的长度有多少个点没有匹配到,
设g[i][j]表示第i个点,它多出来长度为j的可以匹配多少个点,

每次传递完子树信息后,先把f[i][k]匹配完,因为现在不匹配以后就没法匹配了,
剩下的能匹配的尽量匹配,这样一定最优,

复杂度: O(nk)

Code

#include <cstdio>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
using namespace std;
const int N=100005;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,m1,ans;
int B[2*N][2],A[N],B0;
int f[N][22],g[N][22];
void link(int q,int w)
{
    B[++B0][0]=A[q],A[q]=B0,B[B0][1]=w;
    B[++B0][0]=A[w],A[w]=B0,B[B0][1]=q;
}
void dfs(int q,int fa)
{
    f[q][0]=1;
    efo(i,q)if(B[i][1]!=fa)
    {
        dfs(B[i][1],q);
        fo(j,1,m)f[q][j]+=f[B[i][1]][j-1],g[q][j-1]=min(n,g[q][j-1]+g[B[i][1]][j]);
    }
    int t;
    if(f[q][m])
    {
        t=f[q][m]/m1+(f[q][m]%m1!=0);
        ans+=t;
        g[q][m]+=t*m1;
    }
    t=m;
    fod(i,m,0)
        for(;f[q][i];)
        {
            for(;!g[q][t]&&t>i ;t--);
            if(!g[q][t])break;
            int ji=min(g[q][t],f[q][i]);
            g[q][t]-=ji;f[q][i]-=ji;
        }
}
int main()
{
    freopen("repulsed.in","r",stdin);
    freopen("repulsed.out","w",stdout);
    int q,w;
    read(n),read(m1),read(m);
    if(m1==1){printf("%d\n",n);return 0;}
    fo(i,1,n-1)read(q),read(w),link(q,w);
    dfs(1,0);
    q=0;
    fo(i,0,m)q+=f[1][i];
    printf("%d\n",ans+q/m1+(0!=q%m1));
    return 0;
}