Codeforces 1086 简要题解

版权声明:随意转载哦......但还是请注明出处吧: https://blog.csdn.net/dreaming__ldx/article/details/85227952

传送门
这场比赛原地爆炸了啊!!!
只做了两道。
A A 手贱于是 w a wa 了一次,死活调不出错。
题意:给出网格图上三个点坐标,让你求出让三个点连通的最少网格数并且输出任意一种连接的方案。


思路:可以直接上分类讨论。
不过可以人脑减去一些讨论。
我们设最初的坐标为 ( x 1 , y 1 ) ( x 2 , y 2 ) ( x 3 , y 3 ) (x_1,y_1),(x_2,y_2),(x_3,y_3)
然后把 x , y x,y 分别排序变成 x 1 , x 2 , x 3 x_1',x_2',x_3' y 1 , y 2 , y 3 y_1',y_2',y_3'
显然横向走的长度是 x 3 x 1 + 1 x_3-x_1+1 ,我们不妨直接把 ( x 1 , y 2 ) > ( x 3 , y 2 ) (x_1',y_2')->(x_3',y_2') 全部染上。
然后就只用考虑纵向的了。
于是我们找到 y y 最小的点 p ( p x , p y ) p(p_x,p_y) ,把 ( p x , p y ) > ( p x , y 1 1 ) (p_x,p_y)->(p_x,y_1-1) 染上,然后类似地去找到 y y 最大的点 q ( q x , q y ) q(q_x,q_y) ,把 ( q x , q y ) > ( q x , y 1 1 ) (q_x,q_y)->(q_x,y_1-1) 染上就可以满足题意了。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int x[3],y[3],X[3],Y[3];
struct Pot{int x,y;}p[3];
inline bool cmp(const Pot&a,const Pot&b){return a.y==b.y?a.x<b.x:a.y<b.y;}
int main(){
	for(ri i=0;i<3;++i)p[i].x=x[i]=read(),p[i].y=y[i]=read();
	sort(x,x+3),sort(y,y+3),sort(p,p+3,cmp);
	int cnt=0;
	vector<pair<int,int> >ans;
	for(ri i=x[0];i<=x[2];++i)ans.push_back(make_pair(i,y[1]));
	for(ri i=y[0];i<y[1];++i)ans.push_back(make_pair(p[0].x,i));
	for(ri i=y[1]+1;i<=y[2];++i)ans.push_back(make_pair(p[2].x,i));
	cout<<ans.size()<<'\n';
	for(ri i=0;i<ans.size();++i)cout<<ans[i].first<<' '<<ans[i].second<<'\n';
	return 0;
}

B B 简单贪心


题意:给出一棵带权树的边长总和 S S 和树的边,让你任意给每条边分配非负边权使得树的直径最小。


思路:我们考虑只赋值给连向叶子的边权值,且每条边的权值都是 s \frac s{叶子数} ,而其它边都赋值为0.
此时的答案为 2 s \frac{2s}{叶子数} ,不难证明改变任意一条当前边的权值都无法使答案更优,因此答案就是 2 s \frac{2s}{叶子数}
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=1e5+5;
int n,s,cnt=0,du[N];
int main(){
	n=read(),s=read()<<1;
	for(ri i=1;i<n;++i)++du[read()],++du[read()];
	for(ri i=1;i<=n;++i)if(du[i]==1)++cnt;
	printf("%.10lf",(double)s/cnt);
	return 0;
}

C C 贪心好题
题意:给出三个字符串 S , A , B S,A,B 和字符集大小 k k ,问是否存在一种字符集的双射关系使得 S S 映射成的新字符串 S S&#x27; 满足 A S B A\le S&#x27;\le B ,允许输出任意一种方案。


思路:我们根据 A A 字符串逐位贪心确定 S S ,考虑当前 S S 的字符 s i s_i A A 当前的字符 a i a_i

  1. 这个字符之前映射过了,那么直接判断:如果映射值 s i &lt; a i s_i&#x27;&lt;a_i 显然不满足条件;如果 s i = a i s_i&#x27;=a_i 就递归到下一位处理;如果 s i &gt; a i s_i&#x27;&gt;a_i 就直接贪心构造之后的位跟 B B 比大小即可。
  2. 这个字符之前没有映射过,那么先看能否映射成 a i a_i ,如果可以就递归到下一位;否则看能否映射成一个比 a i a_i 大的,如果可以就贪心构造之后的位跟 B B 比较大小。

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=1e6+5;
int n,Tran[26],tran[26],a[N],b[N],s[N],ans[N],vis[26],Vis[26],k;
char S[N];
inline void print(){
	puts("YES");
	for(ri i=0;i<=k;++i)if(Tran[i]==-1)for(ri j=0;j<=k;++j)if(!Vis[j]){Vis[Tran[i]=j]=1;break;}
	for(ri i=0;i<=k;++i)printf("%c",(char)(Tran[i]+'a'));
	puts("");
}
inline bool update(int st){
	for(ri i=0;i<=k;++i)Vis[i]=vis[i],Tran[i]=tran[i];
	for(ri i=st;i<=n;++i){
		if(Tran[s[i]]==-1)for(ri j=0;j<=k;++j)if(!Vis[j]){Vis[Tran[s[i]]=j]=1;break;}
		ans[i]=Tran[s[i]];
	}
	for(ri i=1;i<=n;++i){
		if(ans[i]==b[i])continue;
		if(ans[i]<b[i])return print(),true;
		return false;
	}
	return print(),true;
}
inline bool dfs(int pos){
	if(pos==n+1)return update(pos);
	if(~tran[s[pos]]){
		ans[pos]=tran[s[pos]];
		if(tran[s[pos]]<a[pos])return false;
		if(tran[s[pos]]==a[pos])return dfs(pos+1);
		return update(pos+1);
	}
	bool t;
	if(!vis[a[pos]]){
		vis[ans[pos]=tran[s[pos]]=a[pos]]=1,t=dfs(pos+1);
		if(t)return true;
		tran[s[pos]]=-1,ans[pos]=vis[a[pos]]=0;
	}
	for(ri i=a[pos]+1;i<=k;++i)
		if(!vis[i]){
			vis[ans[pos]=tran[s[pos]]=i]=1,t=update(pos+1);
			if(t)return true;
			return tran[s[pos]]=-1,vis[i]=0;
		}
	return false;
}
int main(){
	for(ri tt=read();tt;--tt){
		k=read()-1;
		scanf("%s",S+1),n=strlen(S+1);
		for(ri i=1;i<=n;++i)s[i]=S[i]-'a';
		scanf("%s",S+1);
		for(ri i=1;i<=n;++i)a[i]=S[i]-'a';
		scanf("%s",S+1);
		for(ri i=1;i<=n;++i)b[i]=S[i]-'a';
		for(ri i=0;i<26;++i)tran[i]=-1,vis[i]=0;
		if(!dfs(1))puts("NO");
	}
	return 0;
}

D D 思维好题
题意: n n 个人排成一排来猜拳,每个人规定每次出的是石头,剪刀或者布(这个规定可以修改)。允许操作 n 1 n-1 次,每次操作可以选当前剩下的两个相邻的人进行比赛,输了的进行淘汰(如果出的相同你可以自己定输赢),每次修改后问有多少人可能赢得最后胜利。


思路 :分情况讨论废话

  1. 所有人都出一样的:答案为 n n
  2. 只出了两种,答案为出较大的人数。
  3. 三种都有出的,发现对于一个人有没有可能赢,只跟左边,右边能否可以消成都不比自己大的有关,因此我们用 s e t set 维护一下每种出法最靠左和最靠右的位置然后容斥一下即可。

代码:

扫描二维码关注公众号,回复: 4707700 查看本文章
#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=2e5+5;
int ans=0,n,q,a[N],bit[N][3];
char s[N];
map<char,int>S;
set<int>pos[3];
inline int lowbit(int x){return x&-x;}
inline void update(int x,int id,int v){for(ri i=x;i<=n;i+=lowbit(i))bit[i][id]+=v;}
inline int query(int x,int id){int ret=0;for(ri i=x;i;i-=lowbit(i))ret+=bit[i][id];return ret;}
set<int>::iterator it;
inline void calc(){
	ans=0;
	for(ri i=0;i<3;++i){
		int x=(i+1)%3,y=(i+2)%3,lx,ly,rx,ry;
		ans+=pos[i].size();
		if(!pos[i].size()||!pos[x].size())continue;
		if(!pos[y].size()){ans-=pos[i].size();continue;}
		lx=*pos[x].begin(),rx=*pos[x].rbegin(),ly=*pos[y].begin(),ry=*pos[y].rbegin();
		if(ly>lx)ans-=query(ly,i)-query(lx-1,i);
		if(ry<rx)ans-=query(rx,i)-query(ry-1,i);
	} 
}
int main(){
	n=read(),q=read(),S['R']=0,S['P']=1,S['S']=2,scanf("%s",s+1);
	for(ri i=1;i<=n;++i)a[i]=S[s[i]],pos[a[i]].insert(i),update(i,a[i],1);
	calc(),cout<<ans<<'\n';
	while(q--){
		int p=read();
		char t[2];
		scanf("%s",t);
		pos[a[p]].erase(p),update(p,a[p],-1);
		a[p]=S[t[0]];
		pos[a[p]].insert(p),update(p,a[p],1);
		calc(),cout<<ans<<'\n';
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/85227952