B. Meeting on the Line
题意:有n nn个人,第i ii个人在x i x_ixi位置,出门需要花t i t_iti时间打扮,故第i ii个人去某地x xx需要花费a b s ( x − x i ) + t i abs(x-x_i)+t_iabs(x−xi)+ti,让你求一个相聚位置x xx,使得所有人聚集的时间最小。
思路:
方法一:二分让所有人聚集的时间。详情看官方题解。
方法二:假设相聚位置为x xx,那么对于位置在x xx的左侧的人的花费是x − x i + t i x-x_i+t_ix−xi+ti,对于位置在x xx的右侧的人的花费是x i − x + t i x_i-x+t_ixi−x+ti,可以维护出两个数组a , b a,ba,b,数组a aa表示t i − x i t_i-x_iti−xi的前缀最大值,数组b bb表示x i + t i x_i+t_ixi+ti的后缀最大值,在位置[ x i , x i + 1 ] [x_i,x_{i+1}][xi,xi+1]中选取一个位置x xx,使得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)(xi−ti,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 mimi,如果当前数d dd大于m i mimi,那么毫无疑问,将该数变为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 nn的字符串s 1 , s 2 s1,s2s1,s2。操作:选一个k , ( 1 ≤ k ≤ n ) k,(1\le k\le n)k,(1≤k≤n),交换s 1 s1s1长度为k kk的前缀和s 2 s2s2长度为k kk的后缀。操作次数不限。问通过若干次变化,是否可以得到两个一样的串。
思路:一整块交换,等价于,若干次一个元素对一个元素的交换。相互交换的元素不能在同一个串中出现,它们构成了一个交换对。长度为n nn的两个串,构成了n nn对交换对(无序)。交换对中的两个元素属于不同的串。当交换对中的其中一个元素的位置确定,那么另一个元素的位置也确定。关系是假设一个元素在第一个串的位置是i ii,那么在另一个元素在第二个串的位置是n − i − 1 n-i-1n−i−1,因为它们是一对交换对,必须是这样的位置关系。所以,先统计一下每种交换对的个数,满足 同种交换对的个数是偶数或者个数是奇数的交换对只有一种并且两个元素是相同的,那么就“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";
}
}