您的位置:首页 > 其它

自建物流的无人机实验(困难)

2015-07-20 18:39 302 查看
题目链接:http://nanti.jisuanke.com/t/440

假设知道最终答案,那么以某点x为根的点对数公式可以列出,其中Cx表示x为根的子树无人机的个数,xi为x的直接子节点:



根据公式可以看出几点:

(1).某个点若满足条件,则只与以该点为根的子树无人机分布情况有关。

(2).根据(1)进一步分析知,解决某点的分配方案需要递归解决该点的所有子树

(3).对公式逆向思维,假设点x为根的树分配个数最少为Cx,那么为了满足题意,则应该使上面公式得出的值尽可能大,而Cx假设确定,即被减数固定,那么应使减数尽量小。所有Cxi的和是Cx,固定。那么,Cxi的方差越小则减数越小。

然后就能做了。

2遍DFS,第一遍确定以x为根的Cx是多少,用优先队列维护子树的Cxi,每次选取Cxi最小的i,即能保证方差最小。第二遍根据求出的Cx,确定每个点是选取还是不选取……

#include <queue>
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long  ll;

#define read freopen("in.txt","r",stdin)
#define N 100020
struct str
{
int n,v;
}ed
;
int head
,cnt;
void init()
{
memset(head,-1,sizeof(head));
cnt = 0;
}
void add(int u,int v)
{
ed[cnt].n = head[u];
ed[cnt].v = v;
head[u] = cnt++;
}
ll a
,b
,c
;
bool vis
;
bool dfs(int u)
{
b[u] = 1, c[u] = 0, vis[u] = false;
ll t = 0;
priority_queue< pair<int,int> >q;
for (int i = head[u]; ~i; i = ed[i].n)
{
int v = ed[i].v;
if(!dfs(v))return false;
b[u] += b[v];
c[u] += c[v];
t -= c[v]*(c[v]-1)/2;
if (c[v] < b[v])q.push(make_pair(-c[v],v));
}
t += c[u]*(c[u]-1)/2;
q.push(make_pair(0,u));

for (;c[u] <= b[u]; ++c[u])
{
if (t >= a[u])return true;
t -= c[u]*(c[u]-1)/2;
t += (c[u]+1)*c[u]/2;
int x = -q.top().first, y = q.top().second;
q.pop();
t += x*(x-1)/2;
t -= (x+1)*x/2;
if (y == u)
{
vis[u] = true;
continue;
}
++ c[y];
if (x+1 < b[y])q.push(make_pair(-x-1,y));
}
return false;
}
void dfs2(int u,ll f)
{
for (int i = head[u]; ~i; i = ed[i].n)
f -= c[ed[i].v];
if (vis[u])--f;

for (int i = head[u]; ~i; i = ed[i].n)
{
int v = ed[i].v;
ll t = min(f,b[v]-c[v]);
dfs2(v,t+c[v]);
f -= t;
c[u] -= (t+c[v]);
}
if (f)vis[u] = true;
}
int main()
{
//read;
int n;
while (~scanf("%d",&n))
{
for (int i = 1; i <= n; ++i)
scanf("%lld",a+i);
init();
int u,v;
for (int i = 1; i < n; ++i)
{
scanf("%d%d",&u,&v);
add(u,v);
}
if(!dfs(1))
{
puts("-1");
continue;
}
dfs2(1,c[1]);
int cnt = 0;
for (int i = 1; i <= n; ++i)
if(vis[i]) cnt++;
printf("%d\n",cnt);
bool ff = false;
for (int i = 1; i <= n; ++i)
if(vis[i])
{
if (ff)printf(" ");
else ff = true;
printf("%d",i);
}
if(cnt)puts("");
}
return 0;
}


这题告诉我们,学好数学很重要,列出公式就能看穿题目……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: