Luogu_P1084流行のコントロールツリーの乗数半分+ +貯金箱

Luogu_P1084流行制御

+ +乗数ツリー###ピギー半分

トピックのリンク
より優れたへの最初のより多くの根本ことは明らかである
すべてのあなたは、乗算器を最適化するためにツリーを使用することができます問題の
答えが単調である
ANS + 1を完了するために、ANS時間ができます明らかにあなた
そして半分回答ANSなり、最大で最低限
、この二分法の答えを確認するにはどのように?
すべてのポイントの最初の子ノードは、ノード上の第1に移動した
移動しない場合は、その後、所定の位置に駐留
あなたは貪欲である新しいものを必要とし、この時間を

彼が戻って、ルートノードから歩くのに十分な部隊を残り時間が来る場合は
、その後、彼は最適な動作を動かないだろう

彼はルートノードに移動した場合なのですることができます彼の距離は、彼のポイントのルートまでの距離よりも小さくなっています
少しRaozui
しかし、また、彼のサブツリーをカバーする新しい点
サブツリーのビットが彼のカバーされた場合に
現在の時点の残りの部分よりも長い証拠の点での残り時間が
より多くの選択肢を持つことができます
どのような愚か者の証拠
残りのコード、コメントがあります


コードは以下の通りであります:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=50010;
bool b=0,stad[maxn],nd[maxn];
int dep[maxn],n,head[maxn],tot,m;
int fa[maxn][35],t;
ll sum=0,a[maxn],dist[maxn][35],tim[maxn],ndd[maxn];
struct node{
    int nxt,to,dis;
    #define nxt(x) e[x].nxt
    #define to(x) e[x].to
    #define dis(x) e[x].dis
}e[maxn<<1];
queue<int> q;
pair<ll,int> pr[maxn];
inline void add(int from,int to,int w){
    to(++tot)=to;dis(tot)=w;
    nxt(tot)=head[from];head[from]=tot;
}
//预处理
inline void bfs(){
    q.push(1);dep[1]=1;
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=nxt(i)){
            int y=to(i);
            if(dep[y]) continue;
            dep[y]=dep[x]+1;
            fa[y][0]=x;dist[y][0]=dis(i);
            for(int j=1;j<=t;j++){
                fa[y][j]=fa[fa[y][j-1]][j-1];
                dist[y][j]=dist[y][j-1]+dist[fa[y][j-1]][j-1];
            }
            q.push(y);
        }
    }
}
//是否要驻扎
bool dfs(int x){
    bool ps=0;
    if(stad[x]) return 1;
    for(int i=head[x];i;i=nxt(i)){
        int y=to(i);
        if(dep[y]<dep[x]) continue;
        ps=1;
        if(!dfs(y)) return 0;
    }
    if(!ps) return 0;
    return 1;
}
inline bool ch(ll tili){
    memset(pr,0,sizeof(pr));
    memset(stad,0,sizeof(stad));
    memset(nd,0,sizeof(nd));
    memset(tim,0,sizeof(tim));
    int prcnt=0,tmcnt=0,ncnt=0;
    for(int i=1;i<=m;i++){
        ll x=a[i],cnt=0;
        for(int j=t;j>=0;j--)
            if(fa[x][j]>1 && cnt+dist[x][j]<=tili){
                cnt+=dist[x][j];x=fa[x][j];
            }
        if(fa[x][0]==1 && cnt+dist[x][0]<=tili)
            pr[++prcnt]=make_pair(tili-cnt-dist[x][0],x);
        else
            stad[x]=1;//就地驻扎
    }//找到空闲的,也就是可以到根节点还有剩余时间的
    for(int i=head[1];i;i=nxt(i))
        if(!dfs(to(i))) nd[to(i)]=1;//需要被覆盖的1的子节点
    sort(pr+1,pr+1+prcnt);
    for(int i=1;i<=prcnt;i++)
        if(nd[pr[i].second] && pr[i].first<dist[pr[i].second][0])//如果不能通过根节点再回来就不能剩余
            nd[pr[i].second]=0;//就地驻扎,就不需要别的点去覆盖
        else tim[++tmcnt]=pr[i].first;//剩余的空余军队多出来的时间数
    for(int i=head[1];i;i=nxt(i))
        if(nd[to(i)]) ndd[++ncnt]=dist[to(i)][0];//需要被覆盖的点的路径边权
    if(tmcnt<ncnt) return 0;//空余军队比需要覆盖的点少,return 0
    sort(tim+1,tim+1+tmcnt);sort(ndd+1,ndd+1+ncnt);
    int i=1,j=1;
    while(i<=ncnt && j<=tmcnt){
        if(tim[j]>=ndd[i]){//到时间之前可以覆盖住两个都向后扫
            i++,j++;
        }else j++;//覆盖不住只能换时间更长的军队
    }
    if(i>ncnt) return 1;//全部扫完了
    else return 0;
}
int main()
{
    scanf("%d",&n);
    for(int x,y,z,i=1;i<n;i++)
        scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z),sum+=z;
    scanf("%d",&m);t=log2(n)+1;
    for(int i=1;i<=m;i++) scanf("%lld",&a[i]);
    bfs();
    ll l=0,r=sum+1,ans=0;
    while(l<=r){
        ll mid=(l+r)>>1;
        if(ch(mid)){
            r=mid-1;ans=mid;b=1;
        }else l=mid+1;
    }//二分需要的最大时间,最大值最小
    if(b) printf("%lld\n",ans);
    else printf("-1\n");
    return 0;
}

おすすめ

転載: www.cnblogs.com/ChrisKKK/p/11635138.html