【思维】NOIP2012疫情控制

来源:互联网 发布:网络播放量排行榜2017 编辑:程序博客网 时间:2024/05/02 00:41

题目链接
当时想这个二分+贪心也是想了半个小时,然而最后还是没想对,木有AC /(ㄒoㄒ)/~~

很明显军队驻扎的城市越靠近根节点(1号节点)越好,然而驻扎在哪个点最好呢?这就需要通过二分最终时间来确定了。

那么问题来了,怎样判断这个mid是合法的?
首先尽可能的将部队往上移动直到下一个达到根节点,难点在于到了根节点又怎么移动?

mintime[i]表示到达节点i的部队中剩余时间最小的,如果这个部队在之后的匹配中能到达i,就不需要考虑他了。
temp1记录那些达到根节点的部队,将他们按照剩余时间从大到小排序。
temp2记录没有部队驻扎的第二层的点,将它们按照离根节点距离从大到小排序。
若点的个数>部队个数,那么肯定不行。再将它们一一匹配,若出现temp1[j].time<temp2[i].time,出现矛盾。

在这里从小到大也可以,只是在判断的时候方式不一样。

具体操作见代码。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 50005#define LL long long int#define max(a,b) ((a)>(b)?(a):(b))using namespace std;int n ,m ,pos[MAXN] ,cnt1 ,cnt2 ,root_son ,fa[MAXN] ,dis[MAXN] ,t[MAXN] ,gra[MAXN] ,mintime[MAXN] ;int a ,b ,c ,l ,r ,mid ,ans ;bool flag[MAXN] ;struct node{    int v ,w ;    node *next ;}edge[MAXN<<1] ,*adj[MAXN] ,*code=edge ;struct aaa{    int v ,rest_time ;    aaa(){}    aaa(const int a,const int b)    {        v=a ,rest_time=b ;    }    bool operator < (const aaa &a)const    {return rest_time>a.rest_time;}}temp1[MAXN] ,temp2[MAXN] ;void add(int a,int b,int c){    ++code;    code->v=b ,code->w=c ,code->next=adj[a] ;    adj[a]=code ;}void dfs2(int u){    int tmp=-1 ,v ;    bool flag1=0 ,flag2=0 ,f=0 ;    for(node *p=adj[u];p!=NULL;p=p->next)        if((v=p->v)!=fa[u])        {            f=1 ;            dfs2(v);            if(t[v]-p->w>=0)flag1=1;//flag1表示以u为根的子树有节点有部队可以继续向上移动            if(t[v]==-1)flag2=1;//flag2表示以u为根的子树有节点没有部队驻扎            tmp=max(tmp,t[v]-p->w);        }    if(u!=1&&f)//u为非叶子节点    {        if(flag1)t[u]=max(t[u],tmp);        else if(flag2)t[u]=max(t[u],-1);        else t[u]=max(t[u],0);    }}bool check(int mid){    memset(t,-1,sizeof t);//t数组表示某部队驻扎在i点的剩余时间    memset(mintime,0,sizeof mintime);    for(int i=1;i<=m;++i)        if(dis[pos[i]]>=mid)            t[pos[i]]=mid ;    dfs2(1);    cnt1=cnt2=0 ;    for(int i=1;i<=m;++i)        if(dis[pos[i]]<mid)            temp1[++cnt1]=aaa(gra[pos[i]],mid-dis[pos[i]]);    sort(temp1+1,temp1+cnt1+1);//这里从小到大排序也可以    for(int i=1;i<=cnt1;++i)    {        int u=temp1[i].v ;        if(mintime[u]==0)mintime[u]=i;        else if(temp1[mintime[u]].rest_time>temp1[i].rest_time)mintime[u]=i;    }    for(node *p=adj[1];p!=NULL;p=p->next)        if(t[p->v]==-1)            temp2[++cnt2]=aaa(p->v,p->w);    sort(temp2+1,temp2+cnt2+1);    if(cnt1<cnt2)return 0;    memset(flag,0,sizeof flag);    int j=1 ;    for(int i=1;i<=cnt2;++i)        if(!flag[mintime[temp2[i].v]]&&mintime[temp2[i].v])            flag[temp2[i].v]=1;        else        {            while(flag[j]&&j<=cnt1)++j;//找到剩余时间最大的且没有在其他城市驻扎的部队            if(j>cnt1)return 0;            if(temp1[j].rest_time<temp2[i].rest_time)return 0;            flag[j++]=1;        }    return 1;}void dfs(int u){    int v ;    for(node *p=adj[u];p!=NULL;p=p->next)        if((v=p->v)!=fa[u])        {            if(u==1)gra[v]=v ;            else gra[v]=gra[u];            fa[v]=u ,dis[v]=dis[u]+p->w ;            dfs(v) ;        }}int main(){    scanf("%d",&n);    for(int i=1;i<n;++i)    {        scanf("%d%d%d",&a,&b,&c);        r+=c ;        add(a,b,c);        add(b,a,c);        if(b==1||a==1)++root_son ;    }    scanf("%d",&m);    if(root_son>m)    {        puts("-1");        return 0;    }    dfs(1);//预处理每一个节点对应的第二层的节点,以及到它的距离    for(int i=1;i<=m;++i)        scanf("%d",&pos[i]);    sort(pos+1,pos+m+1);//不排序也可以,但藐似排了序要快一些    while(l<=r)    {        mid=(l+r)/2 ;        if(check(mid))            ans=mid ,r=mid-1 ;        else l=mid+1 ;    }    printf("%d\n",ans);    return 0;}
1 0
原创粉丝点击