CF1399E2ウェイトディビジョン(ハードバージョン)

E1との違いは、1と2の異なるエッジで動作するコストにあります。
E1の同様の方法に従う場合、それは非常に厄介であるようであり、維持するためにいくつかのスタックを必要とします。
最初に、最終的な答えがどのようにして生じたのかを考えてみましょう。
当然、演算コストが1のエッジに対して演算が1回行われ、演算コストが2のエッジに対して演算が行われる。そして、私たちはaとbを知りません。ただし、これは各操作に最も寄与するエッジで実行する必要があることは確かであり、b時間についても同様です。
次に、コストが1のエッジとコストが2のエッジを2つの部分に分割することを考えることができます。エッジコスト1を何回操作する必要があるかを列挙し、1つの操作に対してエッジコスト2で操作を実行し、2つの点で答えを取得します。一見すると、時間の複雑さはO(n log n)です。(エッジの数がnであるため)
次に、w [i] <= 1e16であるので、各エッジは最大54回操作されるため、最大で540万のエッジO(54n log 54n)があり、これは問題ありません。
ヒープを使用して処理するのではなく、E1を書き込むときに、各エッジを数回削除した後に生成された複数のエッジを処理することを直接考え、それらをまとめて、ソート後にO(n)スイープを直接行う場合、これら2つの質問のコード実装時間を大幅に短縮します。
姫アキコはまだ強い!
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int T,n,s,aa,bb,sum,l,r,mid,hh,ans;
int d[N],size[N],a[54*N],b[54*N];
struct number{
    
    int x,y,w,opt;}num[N];
int cnt,head[N];
struct edge{
    
    int next,to,w;}e[N<<1];

inline void add(int u,int v,int w)
{
    
    
	cnt++;
	e[cnt].next=head[u];
	e[cnt].to=v;
	e[cnt].w=w;
	head[u]=cnt;
}

void dfs(int u,int fa)
{
    
    
	bool jay=true;
	for (register int i=head[u]; i; i=e[i].next)
	if (e[i].to!=fa)
	{
    
    
		jay=false;
		d[e[i].to]=d[u]+1;
		dfs(e[i].to,u);
		size[u]+=size[e[i].to];
	}
	if (jay) size[u]=1; 
}

signed main(){
    
    
	scanf("%lld",&T);
	while (T--)
	{
    
    
		scanf("%lld%lld",&n,&s);
		cnt=0;
		for (register int i=1; i<=n; ++i) head[i]=0;
		d[1]=0;
		for (register int i=1; i<=n; ++i) size[i]=0;
		for (register int i=1; i<n; ++i)
		{
    
    
			scanf("%lld%lld%lld%lld",&num[i].x,&num[i].y,&num[i].w,&num[i].opt);
			add(num[i].x,num[i].y,num[i].w);
			add(num[i].y,num[i].x,num[i].w);	
		}
		dfs(1,0);
		aa=bb=0;
		sum=0;
		for (register int i=1; i<n; ++i)
		{
    
    
			if (d[num[i].x]>d[num[i].y]) swap(num[i].x,num[i].y);
			sum+=size[num[i].y]*num[i].w;
			if (num[i].opt==1)
			{
    
    
				while (num[i].w)
				{
    
    
					int del=num[i].w-num[i].w/2;
					a[++aa]=del*size[num[i].y];
					num[i].w/=2;
				}
			}
			else
			{
    
    
				while (num[i].w)
				{
    
    
					int del=num[i].w-num[i].w/2;
					b[++bb]=del*size[num[i].y];
					num[i].w/=2;
				}
			}
		}
		sort(a+1,a+aa+1); reverse(a+1,a+aa+1);
		sort(b+1,b+bb+1); reverse(b+1,b+bb+1);
		for (register int i=1; i<=aa; ++i) a[i]+=a[i-1];
		for (register int i=1; i<=bb; ++i) b[i]+=b[i-1];
		ans=1e18;
		for (register int i=0; i<=aa; ++i)
		{
    
    
			l=0; r=bb; hh=-1;
			while (l<=r)
			{
    
    
				mid=l+r>>1;
				if (sum-(a[i]+b[mid])<=s) hh=mid,r=mid-1;
				else l=mid+1;	
			}
			if (~hh) ans=min(ans,i+hh*2);
		}
		printf("%lld\n",ans);
	}
return 0;
}

おすすめ

転載: blog.csdn.net/Dove_xyh/article/details/108434415