疫情控制(NOIP2012)庆祝2012满贯!٩(๑•◡-๑)۶ⒽⓤⒼ

丧病至极的D2T3啊!

好吧~

先放个传送门~

原题传送门

好吧,这道题呢。。

根据题意我们可以很明显的看出来

军队往上走的越多(在没到根节点之前),效益一定越大。。

所以可以分情况讨论:

对于无法走到根节点的军队,我们让他走到他能走到的离根节点最近的点

对于可以走到根节点,而且还有剩余时间的点,我们把它记到一个数组里【queen】。

由于第一层(也就是根节点)是无法停留的。

所以我们把能走到根节点的点全部扔到叶子节点还未被覆盖的第二层的点上。

由于结果的单调性,我们可以二分答案。

接下来是重点。

由于queen[]中的所有点都可以到任意一个第二层的点。

所以我们可以设想一个贪心。

把时间剩余最多的点给距离根节点最长的第二层的点。

如果出现找不到的情况,或者不满足的情况,

那么就往大二分,不然就往小二分;

最后的l就是答案啦!

接下来是小贴士:

1、如果你偷懒,不手写比较,而直接用multiset的话,en~你会TLE

2、如果不手打快排的话,你也很容易TLE

3、如果你不打快读的话,你很可能TLE

恩,我三种都没打~

但是过了~

怎么过的呢?~

我们先来看看题目

0<w<10^9..

好大。。

对不对。

但是如果用这个算法的话

时间复杂度为(nlog^2n)

那么是不是看上去不会炸?

可是二分的右端点怎么办?

有的人直接50000*10^9;

然后不写上面的优化全炸TLE

我机智的写了10^8.。

好吧,那是RP好,考场上这么写。。你就等死吧。

下面贴代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<cmath>
#include<algorithm>
using namespace std;
int n,num=0,m,nownode;
int jd[50001];
int head[50001];
int leaf[50001];
int son[50001];
int sontree[50001];
bool visit[50001];
int queen[50001];
multiset<int> S;  
multiset<int>::iterator it; 
int dist[50001][17];
int cnt[50001];
int fa[50001][17];
int Min[50001];
struct duoyu{
    int opt,timen;
}a[50001];
struct edge{
    int next,to,value;
}g[100001];
void ins(int x1,int y1,int v1){
    g[++num].next=head[x1];
    head[x1]=num;
    g[num].to=y1;
    g[num].value=v1;
}
void dfs(int u)
{
    sontree[u]=nownode;
    visit[u]=true;
    bool flag=1;
    for(int i=head[u];i;i=g[i].next){
        int v=g[i].to;
        if(!visit[v])
        {
            flag=false;
            fa[v][0]=u;
            dist[v][0]=g[i].value;
            if(u==1)nownode=v;
            dfs(v);
        }
    }
    leaf[u]=flag;
}
void findleaf(int x,int father){
    if(cnt[x])return;
    if(leaf[x])
    {
        son[nownode]=true;
        return; 
    }
    for(int i=head[x];i;i=g[i].next)
    {
        int v=g[i].to;
        if(v!=father)
        {
            if(x==1)nownode=v;
            findleaf(v,x);
        }    
    }
}
bool check(long long tme){
    S.clear();
    int arm=0;
    memset(son,0,sizeof(son));
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=m;i++)
    {
        long long len=tme;
        int jundui=jd[i];
        for(int j=16;j>=0;j--)
            if(dist[jundui][j]<=len&&fa[jundui][j])
            {
                    len-=dist[jundui][j];
                    jundui=fa[jundui][j];    
            }
        if(jundui==1)
        {
            a[++arm].opt=jd[i];
            a[arm].timen=len;
        }
        else
        cnt[jundui]++;
    }
    findleaf(1,0);
    int tt=0;    
    for(int i=1;i<=n;i++)Min[i]=-1;
    for(int i=1;i<=arm;i++)
    {
        int q=sontree[a[i].opt];
        if(son[q]){
            if(Min[q]==-1||a[Min[q]].timen>a[i].timen)
            Min[q]=i;
        }
    }
    for(int i=1;i<=n;i++)
        if(son[i]&&Min[i]!=-1&&a[Min[i]].timen<dist[i][0])
        a[Min[i]].timen=-1;
        else if(son[i]) queen[++tt]=dist[i][0];
    sort(queen+1,queen+tt+1);
    for(int i=1;i<=arm;i++)
    {
        if(a[i].timen!=-1) S.insert(a[i].timen);
    }
    for(int i=tt;i>=1;i--)
    {
        if(S.lower_bound(queen[i])==S.end()) return false;    
        it=S.lower_bound(queen[i]);
        S.erase(it);    
    }
    return true;
}
void addedge(int x1,int y1,int v1)
{
    ins(x1,y1,v1);ins(y1,x1,v1);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y,v;
        scanf("%d%d%d",&x,&y,&v);
        addedge(x,y,v);
    }
    dfs(1);
    for(int j=1;j<=16;j++)
    for(int i=1;i<=n;i++)
    {
        fa[i][j]=fa[fa[i][j-1]][j-1];
        dist[i][j]=dist[i][j-1]+dist[fa[i][j-1]][j-1];
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)scanf("%d",&jd[i]);    
    long long L=1,R=500000;
    long long ans=-1;  
    while(L<=R){
    long long mid=(L+R)>>1;
    if(check(mid)){R=mid-1;ans= mid;}else L=mid+1;    
    }
    printf("%lld",ans);
    return 0;
}

没错!我最后改成了50万。。!过了!233

可是还是排在倒一(囧。。)

前排膜拜各路大神啊。。

听说拓补+倍增就100ms+..

蒟蒻滚粗。。(%%%zxyer)

原文地址:https://www.cnblogs.com/ghostfly233/p/6842081.html