您的位置:首页 > 其它

NOIP2012 day2 T3 疫情控制

2015-08-12 19:37 369 查看
真是一道难题。观察到题目的时间这个量,时间越大显然覆盖的点越多,所以我们说答案具有单调性,这样便可以二分答案了。

接下来最关键的就是如何检验这一个答案,对于一个答案k:

如果有一些点往回移动k的距离仍然不能达到根节点,我们肯定要尽量的把它往上移,这里采用暴力即可。

有些点可以到根。好吧这个时候呢,把还没有控制的根的儿子节点们拿出来,把可以越过根且没有匹配的军队拿出来跑二分图匹配。复杂度O(nmlogn)显然TLE。

我们可以脑补一种贪心的匹配方法。记R[i]表示i这只军队到达根节点后还剩下的时间,B[j]表示j这个根结点的子节点(第二层节点)到根的时间。

那么一只军队能控制一个子节点当且仅当R[i]>=B[j]。这样的话军队移动的时间为k-(R[i]-B[j]),我们的任务是找到一种匹配方法,使得这个式子的最大值最小。

脑补一下便可发现R[i],B[j]越近越好,所以就两个数组排好序匹配啦,下面给出个人认为正确的证明:

如果一只军队不与他本应(指排好序的)那个数配对,那么R[i]-B[j]可能会变小,如果这次的差没变小也会引起别的i的差变小,所以这个式子的最大值变大。

事实真的如此简单吗?不

如果一只军队可以与自己那条路径上的那个第二层根节点(A)配对,且这个军队如果到达根便无法返回A,那么就让他驻扎在A。

证:如果这只军队去了别的地方x点,然后y军队来填补A,由已知得w(A)>W(x)显然让y去x,这支军队驻扎在A最好。

所以如果存在R[i]<B[j]且也能匹配到自己路上的那个点时,取R[i]值最小的匹配。剩下的实现自己去弄吧。感谢Dash的帮助。%%__debug大神。
#include<cstdio>
#include<queue>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define x first
#define y second
#define pii std::pair<long long,int>
const int MAXN=50005*2;
int first[MAXN],next[MAXN],m,Militar[MAXN],n,fa[MAXN],isLeaf[MAXN];
int e=0,v[MAXN],hasf[MAXN],vis[MAXN],bel[MAXN],f[MAXN];
long long ww[MAXN],dis[MAXN],w[MAXN];
bool use[MAXN];
void add(int a,int b,int c)
{
e++;next[e]=first[a];first[a]=e;ww[e]=c;v[e]=b;
}
bool cmp(const pii& a,const pii& b)
{
return a.x<b.x;
}
void push_up(int u,int k)
{
int sum=0;
vis[u]=1;
while(1)
{
sum+=w[u];
if(sum>k)break;
u=fa[u];if(u==-1)break;
}
vis[u]=1;
}
pii sons[MAXN];
pii R[MAXN];
void calc(int u,int fa,int vt)
{
if(hasf[u])bel[u]=vt;
for(int i=first[u];i!=-1;i=next[i])
{
int to=v[i];
if(to==fa)continue;
calc(to,u,vt);
}
}
bool dfs(int u,int fa)
{
if(isLeaf[u])return 1;
for(int i=first[u];i!=-1;i=next[i])
{
int to=v[i];
if(to==fa)continue;
if(!vis[to])
{
if(dfs(to,u))return 1;
}
}
return 0;
}
void calc_vis(int u,int fa)
{
int flag=1;
if(vis[u])return ;
for(int i=first[u];i!=-1;i=next[i])
{
int to=v[i];
if(to==fa)continue;
calc_vis(to,u);
if(!vis[to])flag=0;
}
if(!isLeaf[u])vis[u]=flag;
}
void clear()
{
memset(vis,0,sizeof(vis));
memset(sons,0,sizeof(sons));
memset(R,0,sizeof(R));
memset(f,0,sizeof(f));
memset(use,0,sizeof(use));
}
int check(long long  k)
{
clear();
int o=1,p=1;
for(int i=1;i<=m;i++)
{
if(dis[Militar[i]]>=k)
push_up(Militar[i],k);
else R[o].x=k-dis[Militar[i]],R[o++].y=Militar[i];
}
calc_vis(1,-1);
for(int i=first[1];i!=-1;i=next[i])
{
int to=v[i];
if(!vis[to])sons[p].x=w[to],sons[p++].y=to,f[to]=p-1;
}
std::sort(R+1,R+o,cmp);
std::sort(sons+1,sons+p,cmp);
int a=1,b=1;
for(int i=1;i<o;i++)
if(vis[bel[R[i].y]]==0)
if(R[i].x<dis[bel[R[i].y]])
vis[bel[R[i].y]]=1,use[i]=1;
for(int i=1;a<o&&b<p;i++)
{
if(use[a]){a++;continue;}
if(vis[sons[b].y]==0)
{
if(R[a].x>=sons[b].x)vis[sons[b].y]=1,a++,b++;
else  a++;
}
else {b++;continue;}
}
return dfs(1,-1);
}
void BFS(int u,int f)
{
fa[u]=f;
if(next[first[u]]==-1)isLeaf[u]=1;
for(int i=first[u];i!=-1;i=next[i])
{
int to=v[i];
if(to==f)continue;
dis[to]=dis[u]+ww[i],w[to]=ww[i];
BFS(to,u);
}
}
int main()
{
scanf("%d",&n);
long long  sum=0;
int cnt=0;
for(int i=0;i<=n;i++)first[i]=-1;
for(int i=1;i<n;i++)
{
int a,b;long long c=0;
scanf("%d %d %I64d",&a,&b,&c);sum+=c;
add(a,b,c);
add(b,a,c);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int a;
scanf("%d",&a);
Militar[i]=a;
hasf[a]=1;
}
BFS(1,-1);
long long  l=0,r=sum;
for(int i=first[1];i!=-1;i=next[i])
calc(v[i],1,v[i]),cnt++;
if(m<cnt){printf("-1");return 0;}
if(!check(0)){printf("0\n");return 0;}
while(l+1!=r)
{
long long  mid=(long long)(l+r)>>1;
if(!check(mid))
r=mid;
else l=mid;
}
printf("%I64d\n",r);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: