zoukankan      html  css  js  c++  java
  • ARC115F

    一棵树每个点有权值(h_i),有(k)个人,初始状态每个人在(s_i),终止状态每个人在(t_i)

    一个状态的代价为(sum h_{a_i}),其中(a_i)表示(i)此时所在位置。

    每个时刻选一个人走一步。要求经过的状态中最大值最小。

    (n,kle 2000)


    如果二分出一个上界(lim),考虑如下做法:让(S)(T)都走的到可到达的状态中的最低点,如果它们相遇了,那就合法。

    把状态以及状态之间的转移看做一个图,显然这是双向的。所以如果规定了唯一的最低点并且它们都能到达,那么它们之间一定能互相到达。

    现在对于每个点(x),求(f_x)表示满足(h_y<h_x)(或(h_y=h_x,y<x))的(y)中满足(x)(y)路径上的最大值最小的(y)(w_x)表示(x o f_x)路径上的最大值。显然((x,f_x))连成了一棵内向森林。

    (S)(或(T))中每次取出(w_x)最小的(x),如果从(x)跳到(f_x)没有超过上限,那就跳过去。由于总和减小,这样操作一定会利于后面的操作,于是一定能到达最低点。

    然后发现其实可以不用二分。就是在(S,T)不重合时,取出(S)(T)(w_x)最小的(x),扩大上界。

    用个堆维护一下,时间(O(nklg n))

    另外还有性质:如果有(f_x=y,f_y=z),则(w_xle w_y)。具体证明可以根据(x,y,z)所在位置分类讨论。所以实际上操作的时候,相当于((x,f_x))连出的树上每次剥叶子。把在同一个叶子上的点一起操作,时间就是(O(nlg n+k))


    using namespace std;
    #include <bits/stdc++.h>
    #define N 2005
    #define ll long long
    #define INF 10000000000000
    #define fi first
    #define se second
    #define mp(x,y) make_pair(x,y)
    int n,K;
    int h[N];
    struct EDGE{
    	int to;
    	EDGE *las;	
    } e[N*2];
    int ne;
    EDGE *last[N];
    void link(int u,int v){
    	e[ne]={v,last[u]};
    	last[u]=e+ne++;
    }
    int s[N],t[N];
    ll mx[N];
    int to[N];
    ll w[N];
    void dfs(int x,int fa,ll s){
    	mx[x]=max(mx[fa],s);
    	for (EDGE *ei=last[x];ei;ei=ei->las)
    		if (ei->to!=fa)
    			dfs(ei->to,x,s+h[ei->to]-h[x]);
    }
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > qs,qt;
    int main(){
    //	freopen("in.txt","r",stdin);
    	scanf("%d",&n);
    	for (int i=1;i<=n;++i)
    		scanf("%d",&h[i]);
    	for (int i=1;i<n;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		link(u,v),link(v,u);
    	}
    	scanf("%d",&K);
    	for (int i=1;i<=K;++i)
    		scanf("%d%d",&s[i],&t[i]);
    	mx[0]=-INF;
    	for (int i=1;i<=n;++i){
    		dfs(i,0,0);
    		for (int j=1;j<=n;++j)
    			if (h[j]<h[i] || h[j]==h[i] && j<i){
    				if (to[i]==0 || mx[to[i]]>mx[j])
    					to[i]=j,w[i]=mx[j];
    			}
    	}
    	int cnt=0;
    	ll S=0,T=0,ans=0;
    	for (int i=1;i<=K;++i){
    		S+=h[s[i]],T+=h[t[i]];
    		if (to[s[i]]) qs.push(mp(w[s[i]],i));
    		if (to[t[i]]) qt.push(mp(w[t[i]],i));
    		cnt+=(s[i]!=t[i]);
    	}
    	ans=max(S,T);
    	while (cnt>0/* && (!qs.empty() || !qt.empty())*/){
    		if (qt.empty() || !qs.empty() && S+qs.top().fi<T+qt.top().fi){
    			int x=qs.top().se;
    			qs.pop();
    			ans=max(ans,S+w[s[x]]);
    			cnt-=(s[x]!=t[x]);
    			S-=h[s[x]];
    			s[x]=to[s[x]];
    			S+=h[s[x]];
    			cnt+=(s[x]!=t[x]);
    			if (to[s[x]])
    				qs.push(mp(w[s[x]],x));
    		}
    		else{
    			int x=qt.top().se;
    			qt.pop();
    			ans=max(ans,T+w[t[x]]);
    			cnt-=(s[x]!=t[x]);
    			T-=h[t[x]];
    			t[x]=to[t[x]];
    			T+=h[t[x]];
    			cnt+=(s[x]!=t[x]);
    			if (to[t[x]])
    				qt.push(mp(w[t[x]],x));
    		}
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    QLPreviewController来预览文件
    Camera360SDK
    实现 JSON + Jquery+.Net ajax功能
    showModalDialog传值
    web方式AJAX调用
    CRM4.0多个实体关联查询
    用javascript 动态添加部门和人员
    GridView中使用超连接
    根据订单号查询出订单附属实体
    sql 常用聚合函数
  • 原文地址:https://www.cnblogs.com/jz-597/p/14569133.html
Copyright © 2011-2022 走看看