Codeforces Round #823 (Div. 2) BCD 题解

B. Meeting on the Line

题意:有 n n n个人,第 i i i个人在 x i x_i xi位置,出门需要花 t i t_i ti时间打扮,故第 i i i个人去某地 x x x需要花费 a b s ( x − x i ) + t i abs(x-x_i)+t_i abs(xxi)+ti,让你求一个相聚位置 x x x,使得所有人聚集的时间最小。
思路:
方法一:二分让所有人聚集的时间。详情看官方题解。
方法二:假设相聚位置为 x x x,那么对于位置在 x x x的左侧的人的花费是 x − x i + t i x-x_i+t_i xxi+ti,对于位置在 x x x的右侧的人的花费是 x i − x + t i x_i-x+t_i xix+ti,可以维护出两个数组 a , b a,b a,b,数组 a a a表示 t i − x i t_i-x_i tixi的前缀最大值,数组 b b b表示 x i + t i x_i+t_i xi+ti的后缀最大值,在位置 [ x i , x i + 1 ] [x_i,x_{i+1}] [xi,xi+1]中选取一个位置 x x x,使得 m a x ( a [ i ] + x , b [ i + 1 ] − x ) max(a[i]+x,b[i+1]-x) max(a[i]+x,b[i+1]x)最小,不难想到 x = m i n ( x [ i + 1 ] , m a x ( ( b [ i + 1 ] − a [ i ] ) / 2 , x [ i ] ) ) x=min(x[i+1],max((b[i+1]-a[i])/2,x[i])) x=min(x[i+1],max((b[i+1]a[i])/2,x[i]))
方法三:将点 ( x i , t i ) (x_i,t_i) (xi,ti)转化为两点 ( x i − t i , 0 ) (x_i-t_i,0) (xiti,0) ( x i + t i , 0 ) (x_i+t_i,0) (xi+ti,0),答案就是(最大坐标+最小坐标)/2。证明见官方题解。

方法二代码:

#include <bits/stdc++.h>
using namespace std;

#define il inline
#define pb push_back
#define fi first
#define se second
#define int long long
#define pii pair<int,int>
const double PI=acos(-1.0);
const double eps=1e-9;
const int mod=1e7+7;
const int N=2e5+5;
const int inf=0x3f3f3f3f;

int T,n;
double x[N],t[N],a[N],b[N];

signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>T;
//	T=1;
	while(T--){
		cin>>n;
		map<double,double> mp;
		for(int i=1;i<=n;++i) {
			cin>>x[i]; 
		}
		double tt;
		for(int i=1;i<=n;++i) {
			cin>>tt;
			mp[x[i]]=max(mp[x[i]],tt);
		}
		if(mp.size()==1){
			for(auto tmp:mp) cout<<fixed<<setprecision(8)<<tmp.fi<<'\n';
		}else{
			int cnt=0;
			for(auto tmp:mp){
				x[++cnt]=tmp.fi;
				t[cnt]=tmp.se;
			}
			a[0]=0-1000000000.0;
			b[cnt+1]=0;
			for(int i=1;i<=cnt;++i)
				a[i]=max(t[i]-x[i],a[i-1]);
			for(int i=cnt;i>=1;--i)
				b[i]=max(t[i]+x[i],b[i+1]);

			double mi=1000000005.0,ans=x[1];
			for(int i=1;i<cnt;++i) {
				double pos=(b[i+1]-a[i])/2;
				if(pos<=x[i]) pos=x[i];
				else if(pos>=x[i+1]) pos=x[i+1]; 
				if(max(a[i]+pos,b[i+1]-pos)<mi) {
					mi=max(a[i]+pos,b[i+1]-pos);
					ans=pos;
				}
			}
			cout<<fixed<<setprecision(8)<<ans<<'\n';
		}	
	}
}

方法三代码:

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N=1e5+5;
int T,n;
double x[N],t[N];

signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;++i) cin>>x[i];
		for(int i=1;i<=n;++i) cin>>t[i];
		double mi=1000000000.0,mx=0-1000000000.0;
		for(int i=1;i<=n;++i) {
			mi=min(x[i]-t[i],mi);
			mx=max(mx,x[i]+t[i]);
		}
		double ans=(mi+mx)/2;
		cout<<fixed<<setprecision(8)<<ans<<'\n';
	}
}

C. Minimum Notation

题意:给你一个字符串。操作:选择串中的一个数d,把它删掉,然后在任意一个位置插入min(9,d+1)。操作可以重复0次或若干次,问最终能得到的最小的字符串是什么。
思路:倒着遍历串,并记录遍历到的数字中的最小数 m i mi mi,如果当前数 d d d大于 m i mi mi,那么毫无疑问,将该数变为min(9,d+1)插到后面会更优,因为这样使整个串保持非递减状态。
代码:

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define fi first
#define se second
int T;
string s;

signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>T;
	while(T--){
		cin>>s;
		map<char,int> mp;
		char mi=s[s.size()-1];
		for(int i=s.size()-1;i>=0;--i){
			if(s[i]>mi){
				if(s[i]<'9') mp[s[i]+1]++;
				else mp[s[i]]++;
			}else{
				mp[s[i]]++;
				mi=s[i];
			}
		}
		for(auto x:mp){
			for(int i=1;i<=x.se;++i) cout<<x.fi;
		}
		cout<<'\n';
	}
}

D. Prefixes and Suffixes

题意:给定两个长度为 n n n的字符串 s 1 , s 2 s1,s2 s1,s2。操作:选一个 k , ( 1 ≤ k ≤ n ) k,(1\le k\le n) k,(1kn),交换 s 1 s1 s1长度为 k k k的前缀和 s 2 s2 s2长度为 k k k的后缀。操作次数不限。问通过若干次变化,是否可以得到两个一样的串。
思路:一整块交换,等价于,若干次一个元素对一个元素的交换。相互交换的元素不能在同一个串中出现,它们构成了一个交换对。长度为 n n n的两个串,构成了 n n n对交换对(无序)。交换对中的两个元素属于不同的串。当交换对中的其中一个元素的位置确定,那么另一个元素的位置也确定。关系是假设一个元素在第一个串的位置是 i i i,那么在另一个元素在第二个串的位置是 n − i − 1 n-i-1 ni1,因为它们是一对交换对,必须是这样的位置关系。所以,先统计一下每种交换对的个数,满足 同种交换对的个数是偶数或者个数是奇数的交换对只有一种并且两个元素是相同的,那么就“YES”,否则“NO”。
代码:

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define se second 
#define fi first
int T,n;
string s1,s2;

signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>s1>>s2;
		string str="";
		map<string,int> mp;
		for(int i=0,j=n-1;i<n;i++,j--){
			str="";
			if(s1[i]>s2[j]) {
				str+=s2[j];
				str+=s1[i];
			}else {
				str+=s1[i];
				str+=s2[j];
			}
			mp[str]++;
		}
		int cnt=0;
		for(auto x:mp){
			if(x.se%2) {
				if(x.fi[0]!=x.fi[1]){
					cnt=2;
					break;	
				}else cnt++;
			}
 		}
 		if(cnt<2) cout<<"YES\n";
		else cout<<"NO\n"; 
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值