Codeforces Round #535 (Div. 3) E1 E2 F题解

纪念下cf第一次ak

E1. Array and Segments (Easy version)

题意:有一个长度为n的序列a,有m次操作,每个操作有个区间可以使得序列a在该区间所有数减一,你可以任意顺序任意选择一些操作,使得序列a(最大值-最小值)最大,求这个最大的值。

思路:我首先把所有区间按照左区间从小到大排序(左区间相同,右区间从小到大排序),然后一个一个暴力更新区间的值,更新(max-min)的值并更新到答案,算完之后再把所有区间按照右区间从大到小排序(右区间相同,左区间从大到小排序),然后同上操作,就可以求出答案了。

update:有群友问我证明,我证明了发现我排序还排复杂了(还可以更简单),第一遍排序为y从小到大排序,第二遍排序为x从大到小排序,假设经过一系列操作后,最优答案是abs(ai-aj),假设aj>ai,那么经过第一类排序,对于所有的[ x , y ](x<=i<=y<j)更新就可取得答案,是不是按照第一类排序顺序就可以做到,假设ai>aj,经过第二类排序,对于所有的[ x , y ](i<x<=j<=y)更新就可以取得答案,我们按照第二类排序顺序就可以做到。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=305;
int n,m,a[maxn],b[maxn],c[maxn],cnt,ans=-1;
struct node
{
	int x,y,id;
}q[maxn];
bool cmp1(node a,node b)
{
	if(a.x==b.x)return a.y<b.y;
	return a.x<b.x;
}
bool cmp2(node a,node b)
{
	if(a.y==b.y)return a.x>b.x;
	return a.y>b.y;
}
void getb()
{
	for(int i=1;i<=n;i++)
	b[i]=a[i];
}
int getv()
{
	int mx=-1e9,mn=1e9;
	for(int i=1;i<=n;i++)
	mx=max(mx,b[i]),mn=min(mn,b[i]);
	return mx-mn;
}
void upans(int res,int tot)
{
	if(res>ans)
	{
		ans=res;
		cnt=tot;
		for(int i=1;i<=cnt;i++)
		c[i]=q[i].id; 
	}
}
void upb(int l,int r)
{
	for(int i=l;i<=r;i++)
	b[i]--;
}
void work()
{
	int res=getv();
	upans(res,0);
	for(int i=1;i<=m;i++)
	{
		upb(q[i].x,q[i].y);
		res=getv();
		upans(res,i);
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=1;i<=m;i++)
	cin>>q[i].x>>q[i].y,q[i].id=i;
	sort(q+1,q+1+m,cmp1); 
	getb();
	work();
	
	sort(q+1,q+1+m,cmp2);
	getb();
	work();
	cout<<ans<<endl;
	printf("%d\n",cnt);
	for(int i=1;i<cnt;i++)printf("%d ",c[i]);
	if(cnt)
	printf("%d\n",c[cnt]);
}

 

E2. Array and Segments (Hard version)

和上题一样,不过n的范围变成了1e5,但是没关系,上个题的代码,数组大小改成1e5照样过,不过我还是写个线段树优化吧,求区间最值,并且区间减一,这个是最简单的lazy型线段树了吧(直接上代码了)..........

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int n,m,a[maxn],c[maxn],cnt,ans=-1;
int mx[maxn*4],mn[maxn*4],tag[maxn*4];
struct node
{
	int x,y,id;
}q[maxn];
bool cmp1(node a,node b)
{
	if(a.x==b.x)return a.y<b.y;
	return a.x<b.x;
}
bool cmp2(node a,node b)
{
	if(a.y==b.y)return a.x>b.x;
	return a.y>b.y;
}
void build(int o,int l,int r)
{
	if(l==r)
	{
		mx[o]=mn[o]=a[l];
		tag[o]=0;
		return;
	}
	int mid=(l+r)/2,ls=o*2,rs=o*2+1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	tag[o]=0;
	mx[o]=max(mx[ls],mx[rs]);
	mn[o]=min(mn[ls],mn[rs]);
}
void up(int o,int l,int r,int ql,int qr)
{
	int mid=(l+r)/2,ls=o*2,rs=o*2+1;
	if(l>=ql&&r<=qr)
	{
		mx[o]--,mn[o]--;
		tag[o]--;
		return;
	}
	if(ql<=mid)up(ls,l,mid,ql,qr);
	if(qr>mid)up(rs,mid+1,r,ql,qr);
	mx[o]=max(mx[ls],mx[rs])+tag[o];
	mn[o]=min(mn[ls],mn[rs])+tag[o];
}
void work()
{
	int tmp=0;
	if(mx[1]-mn[1]>ans)
	tmp=0,ans=mx[1]-mn[1];
	for(int i=1;i<=m;i++)
	{
		up(1,1,n,q[i].x,q[i].y);
		if(mx[1]-mn[1]>ans)
		tmp=i,ans=mx[1]-mn[1];
	}
	if(tmp)
	{
		cnt=tmp;	
		for(int i=1;i<=tmp;i++)
		c[i]=q[i].id;
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=1;i<=m;i++)
	cin>>q[i].x>>q[i].y,q[i].id=i;
	sort(q+1,q+1+m,cmp1); 
	build(1,1,n);
	work();
	sort(q+1,q+1+m,cmp2);
	build(1,1,n);
	work();
	cout<<ans<<endl;
	printf("%d\n",cnt);
	for(int i=1;i<cnt;i++)printf("%d ",c[i]);
	if(cnt)
	printf("%d\n",c[cnt]);
}

F. MST Unification

题意:给你一个无向带权图,每次操作你可以把某些边的权值+1,求最少的操作次数使得该无向图最小生成树唯一。

思路:首先求出最小生成树并且标记用过的边,然后枚举没用过的边u v w,我先在树上用倍增算法找到u  v路径上最大的边权mx,如果mx等于w,那么是不是我可以把那条mx的边删除,用这条边替代,照样可以联通这颗树,且最小生成树权值不变,也就是说,最小生成树不唯一了,那么这条边就必须要改变权值。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+50;
struct node
{
	int u,v,w;
	bool operator<(const node&t)const
	{
		return w<t.w;
	}
}a[maxn];
int par[maxn],vis[maxn],n,m,ans;
int f[maxn][21],mx[maxn][21],dep[maxn];
vector<int>G[maxn],dis[maxn];
int find(int x)
{
	if(x!=par[x])
	par[x]=find(par[x]);
	return par[x];
}
void kruskal()
{
	sort(a+1,a+1+m);
	int res=0;
	for(int i=1;i<=m;i++)
	{
		int u=a[i].u,v=a[i].v;
		int fu=find(u),fv=find(v);
		if(fu!=fv)
		{
			par[fu]=fv;
			vis[i]=1;
			G[u].push_back(v);
			G[v].push_back(u);
			dis[u].push_back(a[i].w);
			dis[v].push_back(a[i].w);
			res++;
			if(res==n-1)break;
		}
	}
}
void dfs(int u,int fa,int w,int deep)
{
	f[u][0]=fa;
	mx[u][0]=w;
	dep[u]=deep;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa)continue;
		dfs(v,u,dis[u][i],deep+1);
	}
}
void init()
{
	for(int i=1;i<=20;i++)
	for(int j=1;j<=n;j++)
	{
		int fa=f[j][i-1];
		f[j][i]=f[fa][i-1];
		mx[j][i]=max(mx[j][i-1],mx[fa][i-1]);
	}
}
int lca(int p,int q)
{
	if(dep[p]<dep[q])swap(p,q);
	int res=0;
	for(int i=20;i>=0;i--)
	if(dep[f[p][i]]>=dep[q])
	res=max(res,mx[p][i]),p=f[p][i];
	if(p==q)return res;
	for(int i=20;i>=0;i--)
	if(f[p][i]!=f[q][i])
	{
		res=max(res,mx[p][i]);
		res=max(res,mx[q][i]);
		p=f[p][i],q=f[q][i];
	}
	return max(res,max(mx[p][0],mx[q][0]));
}
int work()
{
	for(int i=1;i<=m;i++)
	if(!vis[i])
	{
		int w=lca(a[i].u,a[i].v);
		if(w==a[i].w)
		ans++; 
	}
	return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	par[i]=i;
	for(int i=1;i<=m;i++)
	scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
	kruskal();
	dfs(1,0,0,1);
	init();
	printf("%d\n",work());
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值