ICPC第一场网络赛补题 D

ICPC第一场网络赛补题 D

题目链接link.

好后悔场上最后二十分钟才看这个题呜呜呜

题意:n个点,m次操作,每次操作给一个 l l l 给一个 r r r 一个 w w w, 给区间 [ l , r ] [l,r] [l,r] 内所有的点对之间都连一条权值为 w w w 的边,问你m次操作后,如果有点和1没有连起来,则输出“Gotta prepare a lesson”,否则输出,能删除的边的最大权值,使得这个图还是连通图。

这题,后期都过烂了。可惜因为我之前K题那个傻逼模拟犯了一些傻逼错误一直wa,就没心思开新的题。最后连过两题发现开的题都过了,没题目做了,再一看榜我超这题怎么过了这么多。

具体的做法,我们可以反向考虑。算出这个联通图的最小的权值和。先说一下我的大常数必须要带快读的做法。做法就是用线段树维护区间min,我们可以知道,最后删完边之后最小连通图的分布一定可以满足 1连2,2连3, 3连4…这样子。所以我们只需要维护一颗大小为 n − 1 n-1 n1 的线段树。每次给的操作,都让对应的线段与操作给出的w取最小值就好。但是这样做不好维护sum,只能最后query(n-1)次再求和了。问题就在这里,这样查询n-1次可能常数就比较大了…不过加加快读还是可以过去的。

然后捏,还有一种优化的做法,就是我们不维护min了,我们之间修改区间值并且维护sum。首先,我们可以考虑离线的做,把m次操作按照w的值从大到小排序。然后先区间修改w大的。这样可以直接修改,因为你肯定会越改越小嘛,对吧。而且这样就很好维护sum了,常数会小一些。

AC代码:

#include<iostream>
#include<cstdio>
using namespace std;
const long long inf=99999999;

long long lread()
{
    long long 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;
}

struct QwQ
{
	long long l,r;
	long long value;
	long long lazy_tag=inf;
}tree[4*100010];

void build(long long x,long long l,long long r)
{
	tree[x].l=l;
	tree[x].r=r;
	tree[x].lazy_tag=inf;
	if(l==r)
	{
		tree[x].value=inf;
		return ;
	}
	long long mid=(l+r)/2;
	build(x*2,l,mid);
	build(x*2+1,mid+1,r);
	tree[x].value=min(tree[x*2].value,tree[x*2+1].value);
}
void update(long long x,long long w)
{
	tree[x].value= min(tree[x].value,w);
	tree[x].lazy_tag= min(tree[x].lazy_tag,w);
	//tree[x].lazy_tag=w;
	return ;
}
void push_down(long long x)
{
	update(x*2,tree[x].lazy_tag);
	update(x*2+1,tree[x].lazy_tag); 
	tree[x].lazy_tag=inf;
}
void push_up(long long x)
{
	tree[x].value=min(tree[x*2].value ,tree[x*2+1].value);
}
void change(long long x,long long l,long long r,long long w)
{	
	if(tree[x].l==l&&tree[x].r==r)
	{
		update(x,w);
		return ;
	}
	if(tree[x].lazy_tag!=inf) push_down(x);
	
	long long mid=(tree[x].l+tree[x].r)/2;
	if(r<=mid) change(x*2,l,r,w);
	else if(l>=mid+1) change(x*2+1,l,r,w);
	else
	{
		change(x*2,l,mid,w);
		change(x*2+1,mid+1,r,w);
	}
	push_up(x);
}
long long query(long long x,long long l,long long r)
{
	if(tree[x].l==l&&tree[x].r==r)
	{
		return tree[x].value;	
	}
	
	if(tree[x].lazy_tag!=0) push_down(x);
	long long mid=(tree[x].l+tree[x].r)/2;
	if(r<=mid)
	{
		return query(x*2,l,r);
	}
	else if(l>=mid+1)
	{
		return query(x*2+1,l,r);
	}
	else
	{
		return min(query(x*2,l,mid),query(x*2+1,mid+1,r));
	}
} 

int main( )
{
	int t;
	scanf("%d",&t);
	for(int haha=1;haha<=t;haha++)
	{
		long long n,m;
		//scanf("%lld%lld",&n,&m);
		n=lread();
		m=lread();
		long long sum=0;
		build(1,1,n-1);
		//a[k]代表[k,k+1)这一段的值
		
		for(int xixi=1;xixi<=m;xixi++)
		{
			long long l,r,w;
			//scanf("%lld%lld%lld",&l,&r,&w);
			l=lread();r=lread();w=lread();
			sum=sum+ (r-l+1)*(r-l)*w/2;
			change(1,l,r-1,w);
		}
		
		long long cnt=0;
		int flag=0;
		for(long long xixi=1;xixi<=n-1;xixi++)
		{
			long long wuwu=query(1,xixi,xixi);
			cnt=cnt+ wuwu;
			
			if(wuwu == inf)
			flag=1;
		}
		long long ans=sum-cnt;
		printf("Case #%d: ",haha);
		
		if(flag==1) printf("Gotta prepare a lesson");
		else printf("%lld",ans);
		
		if(haha!=t) printf("\n");
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值