您的位置:首页 > 其它

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

2017-10-07 19:49 459 查看

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
,B0;
int f
[22],g
[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: