题目链接: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[N]; int head[N],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[N],b[N],c[N]; bool vis[N]; 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 ",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; }
这题告诉我们,学好数学很重要,列出公式就能看穿题目……