Codeforces Round #535 (Div. 3) A(签到)B(暴力)C(暴力)D(暴力)E1(暴力)E2(暴力线段树)F(并查集改编)

A. Two distinct points(签到)

题目链接:https://codeforces.com/contest/1108/problem/A

题目大意:在一个数轴上,有两个线段,这两条线段可能重合||相交||分离。我们从这两个线段中选择两个不重复的点,然后输出即可。

思路:就是一个直接输出的题??!!比较一下,然后输出就行了。

AC:

int main(){
	int T;
	while(cin>>T){
		while(T--){
			int a,b,c,d;
			cin>>a>>b>>c>>d;
			if(a!=c)	cout<<a<<" "<<c<<endl;
			else if(b!=d)	cout<<b<<" "<<d<<endl;
			else	cout<<a<<" "<<b<<endl;
		}
	}
}

B. Divisors of Two Integers(暴力)

题目链接:https://codeforces.com/contest/1108/problem/B

题目大意:你有两个数x,y,然后是一个数组a,这个数组中包含了这两个数的所有因数,如果有相同因数的话,都要加进去。问x,y分别是什么。

思路:两个最大的数就是因数了,首先找到最大的,必定是x,然后把x的因数去除,最大的就是y了

AC:

int arr[MAXN];

int main(){
	int n;
	while(cin>>n){
		for(int i=1;i<=n;++i){
			cin>>arr[i];
		}
		sort(arr+1,arr+n+1);
		int ansa=arr[n],ansb;
		for(int i=n-1;i>=1;--i){
			if(ansa%arr[i]!=0||arr[i]==arr[i+1]){
				ansb=arr[i];
				break;
			}
		}
		cout<<ansa<<" "<<ansb<<endl;
	}
}

C. Nice Garland(暴力)

题目链接:https://codeforces.com/contest/1108/problem/C

题目大意:三种颜色的灯泡,R,G,B,如果两个同样的灯泡之间距离正好=3,就是说(kindi=kindj&&abs(i-j)%3==0),就说这个排列时好的。问你如何改变这一个序列,使用最少的修改颜色数,让这个序列变成好序列。

思路:三种灯,排列组合有6种,我是直接对于每种排列组合直接暴力了,然后从中选出最少的那种组合。

AC:

char res[6][4]={"RGB","RBG","GRB","GBR","BGR","BRG"};

int main(){
	int n;
	string s="";
	while(cin>>n){
		cin>>s;
		int ansi,ans=INF32;
		for(int i=0;i<6;++i){
			int e=0;
			for(int j=0;j<n;++j){
				if(res[i][j%3]!=s[j])
					e++;
			}
			if(e<ans){
				ansi=i;
				ans=e;
			}
		}
		cout<<ans<<endl;
		for(int i=0;i<n;++i){
			cout<<res[ansi][i%3];
		}cout<<endl;
		s="";
	}
}

D. Diverse Garland(暴力)

题目链接:https://codeforces.com/contest/1108/problem/D

题目大意:C的变形,仍是三种颜色的灯。我们现在认为相邻的灯的颜色不一样就是好的序列。问原序列经过最少多少次染色后能变成好序列。

思路:直接暴力,遍历一遍,只用看后面的用不用染成其他颜色。就行了。

AC:

int main(){
	int n;char s[MAXN];
	while(cin>>n){
		clean(s,'\0');
		cin>>s;int cnt=0;
		for(int i=1;i<n;++i){
			if(s[i]==s[i-1]){
				if(s[i]=='R'){//red
					if(s[i+1]=='R')			s[i]='G';
					else if(s[i+1]=='G')	s[i]='B';
					else					s[i]='G';
					cnt++;
				}
				else if(s[i]=='G'){//green
					if(s[i+1]=='R')			s[i]='B';
					else if(s[i+1]=='G')	s[i]='B';
					else					s[i]='R';
					cnt++;
				}
				else{//bule
					if(s[i+1]=='R')			s[i]='G';
					else if(s[i+1]=='G')	s[i]='R';
					else					s[i]='G';
					cnt++;
				}
			}
		}
		cout<<cnt<<endl;
		for(int i=0;i<n;++i){
			cout<<s[i];
		}cout<<endl;
	}
}

E1. Array and Segments (Easy version)(暴力)

题目链接:https://codeforces.com/contest/1108/problem/E1

题目大意:给你一个数组,和m个区间,问你从这些区间种选择一些区间,让这些区间内的数每个-1.(每个区间都操作一次)。问最后能得出来最大的数(数组中最大的数-最小的数)是多少。并且输出所选择的区间个数和区间。

思路:简单版本的数据范围都好小,直接 for for for 就暴力掉了,把所有的认为最大值和认为最小值都选一便就行了。

AC:

int arr[MAXN],line[310][2];

int main(){
	int n,m;
	while(cin>>n>>m){
		for(int i=1;i<=n;++i){
			cin>>arr[i];
		}
		for(int i=1;i<=m;++i){
			cin>>line[i][0]>>line[i][1];
		}
		int ansi,ansj,ansline,ans=-INF32;
		for(int i=1;i<=n;++i){// choose a dot become the maxval dot
			for(int j=1;j<=n;++j){//choose a different dot become the minval dot
				int res=arr[i]-arr[j],resline=0;
				for(int k=1;k<=m;++k){//check all edge,find onle reduce(contain) the minval dot's edge
					if((line[k][0]>i||line[k][1]<i)&&(line[k][0]<=j&&line[k][1]>=j)){
						res++;resline++;
					}
				}
				if(res>ans){
					ansi=i;ansj=j;
					ans=res;ansline=resline;
				}
			}
		}
		cout<<ans<<endl<<ansline<<endl;
		for(int i=1;i<=m;++i){
			if((line[i][0]>ansi||line[i][1]<ansi)&&(line[i][0]<=ansj&&line[i][1]>=ansj)){
				cout<<i<<" ";
			}
		}cout<<endl;
	}
}

AC2:学习E2的题解的时候看到另一种方法,据他说是O(n(n+m))但是看着循环有点像O(n(n+nm)).....

int arr[MAXN],l[MAXN],r[MAXN],b[MAXN];
vector<int> v,ans;
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>arr[i];
	}
	for(int i=1;i<=m;++i){
		cin>>l[i]>>r[i];
	}
	ll val=-INF64;
	for(int i=1;i<=n;++i){
		v.clear();
		ll maxx=-INF64,minn=INF64;
		for(int j=1;j<=n;++j){
			b[j]=arr[j];// initialize arr_b
		}
		for(int j=1;j<=m;++j){
			if(l[j]>i||r[j]<i){// this point is not in the line
				v.push_back(j);//line_j can be accept
				for(int k=l[j];k<=r[j];++k)//traverse all points belong the line_j
					b[k]--;	//val_k will be minus
			}
		}
		for(int i=1;i<=n;++i){//traverse all points.and get the maxx,minn num
			if(b[i]<minn)	minn=b[i];
			if(b[i]>maxx)	maxx=b[i];
		}
		if(maxx-minn>val)	ans=v;//refresh ans
		val=max(val,maxx-minn);//refresh val
	}
	cout<<val<<endl;
	cout<<ans.size()<<endl;
	vector<int>::iterator it;
	it=ans.begin();
	for(;it!=ans.end();++it){
		cout<<*it<<" ";
	}cout<<endl;
}

E2. Array and Segments (Hard version)

题目链接:https://codeforces.com/contest/1108/problem/E2

题目大意:题意和E1一样,只不过数据范围变大了

思路:我们写E1的时候,用的n*n对于每个n*n找m,所以是n*n*m,现在肯定不能了,我就想,只遍历1边,找最小的那个,最大的那个用线段树来维护,每次查询的时候,直接查询最大的,然后和当前这个点相减,判断是否能够刷新ans,对于每个最小的点,遍历所有的边一次。总体来说是O(n*m*logn)的复杂度,其实我觉得这个也有点玄。。但没想到竟然过了。。

AC:

//#pragma comment(linker, "/STACK:1024000000,1024000000") 
 
#include<stdio.h>
#include<string.h>  
#include<math.h>  
  
#include<map>   
//#include<set>
#include<deque>  
#include<queue>  
#include<stack>  
#include<bitset> 
#include<string>  
#include<fstream>
#include<iostream>  
#include<algorithm>  
using namespace std;  
 
#define ll long long  
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b) 
#define clean(a,b) memset(a,b,sizeof(a))// ?? 
//std::ios::sync_with_stdio(false);
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0); 

ll tree[MAXN<<2],add[MAXN<<2];
int arr[MAXN],l[310],r[310];
vector<int> vec[MAXN];

void intt(){
	clean(tree,0);clean(add,0);
	clean(arr,0);clean(l,0);clean(r,0);
	for(int i=0;i<MAXN;++i)
		vec[i].clear();
}
void build_tree(int l,int r,int rt){
	if(l==r){
		tree[rt]=arr[l];
		//cout<<"point "<<l<<": "<<tree[rt]<<endl;
		return ;
	}
	int mid=(l+r)>>1;
	build_tree(l,mid,rt<<1);
	build_tree(mid+1,r,rt<<1|1);
	tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
void push_down(int rt){
	if(add[rt]!=0){
		add[rt<<1]+=add[rt];
		add[rt<<1|1]+=add[rt];
		
		tree[rt<<1]+=add[rt];
		tree[rt<<1|1]+=add[rt];
		
		add[rt]=0;
	}
}
void updata(int ql,int qr,int x,int l,int r,int rt){
	if(ql<=l&&qr>=r){
		tree[rt]+=x;add[rt]+=x;
		return ;
	}
	int mid=(l+r)>>1;
	push_down(rt);
	if(ql<=mid)	updata(ql,qr,x,l,mid,rt<<1);
	if(qr>mid)	updata(ql,qr,x,mid+1,r,rt<<1|1);
	tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
int Query(int ql,int qr,int l,int r,int rt){
	if(ql<=l&&qr>=r)	return tree[rt];
	int mid=(l+r)>>1;
	push_down(rt);
	int res=-INF32;
	if(ql<=mid)	res=Query(ql,qr,l,mid,rt<<1);
	if(qr>mid)	res=Query(ql,qr,mid+1,r,rt<<1|1);
	return res;
}
void Show(int l,int r,int rt){
	//cout<<"l~r"<<l<<" "<<r<<endl;
	if(l==r){
		cout<<"point: "<<l<<": "<<tree[rt]<<endl;
		return ;
	}
	int mid=(l+r)>>1;
	Show(l,mid,rt<<1);
	Show(mid+1,r,rt<<1|1);
}
int main(){
	int n,m;
	while(cin>>n>>m){
		intt();
		for(int i=1;i<=n;++i){
			cin>>arr[i];
		}
		for(int i=1;i<=m;++i){
			cin>>l[i]>>r[i];
			vec[l[i]].push_back(i);
			vec[r[i]+1].push_back(-i);
		}
		int ans=-INF32,ansi;
		build_tree(1,n,1);
		for(int i=1;i<=n;++i){// 对每个点都试一边最小值 
			int len=vec[i].size();
			for(int j=0;j<len;++j){
				int x=vec[i][j];
				if(x>0)	updata(l[x],r[x],-1,1,n,1);
				else	updata(l[-x],r[-x],1,1,n,1);
			}
			int maxx=Query(1,n,1,n,1);
			int minn=Query(i,i,1,n,1);
			if(maxx-minn>ans){
				ans=maxx-minn;
				ansi=i;
			}
//			Show(1,n,1);cout<<tree[1]<<endl;
//			cout<<"maxx,minn,ans:"<<maxx<<" "<<minn<<" "<<ans<<endl;
//			cout<<"-----"<<endl;
		}
		int k=0;
		for(int i=1;i<=m;++i){
			if(l[i]<=ansi&&r[i]>=ansi)
				k++;
		}
		cout<<ans<<endl<<k<<endl;
		for(int i=1;i<=m;++i){
			if(l[i]<=ansi&&r[i]>=ansi)
				cout<<i<<" ";
		}cout<<endl;
	}
}

 
/*

*/

F. MST Unification(并查集改编)

题目链接:https://codeforces.com/contest/1108/problem/F

题目大意:n个点,m条边,问你修改其中的一些边(边权+1),使得这个图的最小生成树只有一种。

思路:并查集,然后修改一下就行了,说实话我这个使看题解才看出来并查集的。。只不过题解的代码好懵逼啊,我们在并查集中加一点,就是得出所有边权都相同的边,然后从中选出,符合要求的边,其余的边(除了都不选的)就是我们要修改的边了。

AC:

const int MAXN=2e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);

struct node{
	int s,t,val;
}edge[MAXN];
int f[MAXN];
void intt(){
	for(int i=0;i<MAXN;++i){
		f[i]=i;
	}
}
int cmp(node a,node b){
	return a.val<b.val;
}
int Find(int x){
	int pre=x,nxt=x;
	while(f[x]!=x){
		x=f[x];
	}
	while(f[pre]!=x){
		nxt=f[pre];
		f[pre]=x;
		pre=nxt;
	}
	return x;
}
void mix(int x,int y){
	int xx=Find(x),yy=Find(y);
	f[xx]=yy;
}
int main(){
	int n,m;
	while(cin>>n>>m){
		intt();
		for(int i=1;i<=m;++i){
			cin>>edge[i].s>>edge[i].t>>edge[i].val;
		}
		sort(edge+1,edge+1+m,cmp);
		int ans=0;
		for(int i=1,j=1;i<=m;i=j){
			while(j<=m&&edge[i].val==edge[j].val)	++j;
			int res=j-i;//at the begin assume there are (j-i) same edgeval
			for(int k=i;k<j;++k){
				int x=edge[k].s,y=edge[k].t;
				if(Find(x)==Find(y)){//if the edge is exist,we don't choose the edge
					res--;
				}
			}
			for(int k=i;k<j;++k){
				int x=edge[k].s,y=edge[k].t;
				if(Find(x)!=Find(y)){//we must connect the edge
					res--;
					mix(x,y);
				}
			}
			ans+=res;
		}
		cout<<ans<<endl;
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值