[AtCoder Regular Contest 098]F - Donation

Portal
挺套路的。。。
考虑倒过来操作。假设最后在点 x x x上,剩余的钱为 a n s ans ans,那么 a n s ans ans一定不小于 A x − B x A_x-B_x AxBx,先假定 W = a n s = A x − B x W=ans=A_x-B_x W=ans=AxBx。之后让 W + = B x W+=B_x W+=Bx,然后若 y y y x x x直接相连,并且 W W W不小于 A y − B y A_y-B_y AyBy,就可以让 W + = B y W+=B_y W+=By,可若 W W W小于,又想去到 y y y,那么就让 a n s = A y − B y − ( W − a n s ) ans=A_y-B_y-(W-ans) ans=AyBy(Wans) W = A y W=A_y W=Ay。最后当把所有点都遍历完后,答案就为 a n s + ∑ i = 1 n B i ans+\sum_{i=1}^nB_i ans+i=1nBi
注意到点的限制变成了 A i − B i A_i-B_i AiBi,所以设 C i = m a x ( A i − B i , 0 ) C_i=max(A_i-B_i,0) Ci=max(AiBi,0)。同时注意到上述限制类似于 k r u s k a l kruskal kruskal重构树,不过不同的是现在是走的 C i C_i Ci在不断变大。 y y yy yy一下可以发现,可以将边按照 m a x ( C x , C y ) max(C_x,C_y) max(Cx,Cy)从小到大排序后,做一遍重构树即可。最后只需要树形 d p dp dp就可以统计答案了,具体来说就是 f ( x ) = m a x ( f ( f a ( x ) ) , C f a ( x ) − t o t ( x ) ) f(x)=max(f(fa(x)),C_{fa(x)}-tot(x)) f(x)=max(f(fa(x)),Cfa(x)tot(x)) t o t ( x ) tot(x) tot(x)表示以 x x x为根的子树内的 B i B_i Bi和)。最终答案就是 m i n i = 1 n ( m a x ( C i , f ( i ) ) ) + t o t ( r t ) min_{i=1}^n(max(C_i,f(i)))+tot(rt) mini=1n(max(Ci,f(i)))+tot(rt)
所以这道题就做完了,时间复杂度是 O ( m l o g m + n l o g n ) O(mlogm+nlogn) O(mlogm+nlogn)的,可以轻松通过。

#include
#include
#include
#include
#include
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
	if(!x){putchar('0');return;}
	int top=0;
	while(x)stack[++top]=x%10,x/=10;
	while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x),putchar(' ');}
inline void pr2(int x){write(x),puts("");}
struct node
{
	int x,y,next;
}e[100010];int len,last[100010];
inline void ins(int x,int y)
{
	len++;
	e[len].x=x;e[len].y=y;
	e[len].next=last[x];last[x]=len;
}
long long a[100010],b[100010];
bool cmp(node u,node v){return a[u.x]<a[v.x];}
int rt,fa[100010];
inline int findfa(int x)
{
	if(fa[x]!=x)fa[x]=findfa(fa[x]);
	return fa[x];
}
long long uf,tot[100010],s[100010];
inline void dfs(int x)
{
	for(int k=last[x];k;k=e[k].next)
	{
		int y=e[k].y;
		s[y]=max(s[x],a[x]-tot[y]);
		dfs(y);
	}
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int n=read(),m=read();
    for(int i=1;i<=n;i++){a[i]=read(),b[i]=read();a[i]=max(0LL,a[i]-b[i]),uf+=b[i];}
    for(int i=1;i<=m;i++)
    {
    	int x=read(),y=read();if(a[x]<a[y])swap(x,y);
    	e[i].x=x,e[i].y=y;
    }sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++)fa[i]=i,tot[i]=b[i];
    for(int i=1;i<=m;i++)
    {
    	int fx=findfa(e[i].x),fy=findfa(e[i].y);
    	if(fx!=fy){fa[fy]=fx,rt=fx,tot[fx]+=tot[fy];ins(fx,fy);}
    }dfs(rt);
    long long ans=max(a[1],s[1])+uf;
    for(int i=2;i<=n;i++)ans=min(ans,max(a[i],s[i])+uf);
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(生成树,并查集,dp)