A.Linear Keyboard
题目描述
某键盘的 26 26 26个字母键排成一行,顺序是(长度为 26 26 26 的)字符串 s s s,现要打出字符串 t t t,需要在键盘上移动几个格子?例如在 a b … z ab\dots z ab…z 键盘上打出 a c e ace ace 需要移动 2 + 2 = 4 2+2=4 2+2=4 格。
分析
记录每个字符的位置,对字符串 s s s。计算 ∑ i = 1 i < s . s i z e ( ) i d [ s [ i ] ] − i d [ s [ i − 1 ] ] \sum_{i=1}^{i<s.size()}id[s[i]]-id[s[i-1]] ∑i=1i<s.size()id[s[i]]−id[s[i−1]]
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+100;
int mp[maxn];
int n,m;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
string s;cin>>s;
for (int i=0;i<s.size();++i)
mp[s[i]] = i;
cin>>s;int ans = 0;
for (int i=1;i<s.size();++i)
{
ans += abs(mp[s[i]]-mp[s[i-1]]);
}cout<<ans<<endl;
}
}
B.Odd Grasshopper
题目描述
数轴上,给一起始坐标点 x x x。在第 i i i秒,如果此时坐标为奇数则 x + = i x+=i x+=i,否则 x − = i x-=i x−=i
问第 n n n秒末的位置。
分析
可知在经历了 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4秒后,坐标 x x x停在了原地
即,从一个奇数秒 i i i开始: + i − ( i + 1 ) − ( i + 2 ) + ( i + 3 ) = 0 +i-(i+1)-(i+2)+(i+3) = 0 +i−(i+1)−(i+2)+(i+3)=0 不变
因此我们只需关注最后三秒即可
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
ll x,n;
cin>>x>>n;
for (ll i = n/4*4+1;i<=n;++i)
{
if (x%2)x += i;
else x -= i;
}
cout<<x<<endl;
}
}
C.Minimum Extraction
题目描述
对于数组 a a a,可以进行以下操作任意次:删除最小的元素 m m m,然后将所有其他元素都减少 m m m
使得数组的最小元素最大
分析
如果最小元素为一个负数,那么所有的数删除掉最小元素后都变大了。
也就是说,只要最小元素还是负数,且数组大小不为 1 1 1,那么我就一直删除
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;
ll a[maxn];
int n;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
cin>>n;
for (int i=1;i<=n;++i)cin>>a[i];
sort(a+1,a+1+n);
ll cur = 0;ll ans = a[1];
for (int i=1;i<=n;++i)
{
a[i] = a[i] + cur;
cur -= a[i];
ans = max(ans, a[i]);
}cout<<ans<<endl;
}
}
D.Blue-Red Permutation
题目描述
现有一数组,每个元素都有一种颜色(蓝色或红色)。
- 若为蓝色,则你可以将其元素值减少任意一个正整数;
- 若为红色,则你可以将其元素值增大任意一个正整数。
请问,若干次操作后,是否可以将数组变为 1 1 1到 n n n 的一种排列。
分析
贪心的想法。我们可以分别处理蓝色的数字和红色的数字
蓝色的数字,我们按照原数字从小到大排序后,优先填充构造 1 , 2 , 3 , … , n 1,2,3,\dots,n 1,2,3,…,n
然后对于红色的数字我们按照数字从大到小排序后,优先填充构造 n , n − 1 , n − 2 , … , 1 n,n-1,n-2,\dots,1 n,n−1,n−2,…,1
最终看是否可以全部填充完毕
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;
ll a[maxn];
int n;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
cin>>n;
for (int i=1;i<=n;++i)cin>>a[i];
vector<int> v1,v2;
for (int i=1;i<=n;++i)
{
char ch;cin>>ch;
if (ch=='B')
{
if (a[i]<1)continue;
if (a[i]>n)a[i]=n;
v1.push_back(a[i]);
}
else
{
if (a[i]>n)continue;
if (a[i]<1)a[i]=1;
v2.push_back(a[i]);
}
}sort(v1.begin(),v1.end());
sort(v2.begin(),v2.end());
reverse(v2.begin(),v2.end());
if (v1.size()+v2.size()!=n)
{
cout<<"NO\n";
continue;
}
int l = 0;bool f = true;
for (int i=0;i<v1.size();++i)
{
if (l+1<=v1[i])
{
++l;
}else
{
f = false;
break;
}
}if (!f)
{
cout<<"NO\n";
continue;
}
int r = n+1;
for (int i=0;i<v2.size();++i)
{
if (r-1>=v2[i])--r;
else
{
f = false;
break;
}
}
if (!f)
{
cout<<"NO\n";
continue;
}cout<<"YES\n";
}
}
E.Robot on the Board 1
题目描述
现有一串由 U , D , L , R U,D,L,R U,D,L,R 组成的指令,分别对应上、下、左、右的移动。机器人在 n × m n\times m n×m 大小的棋盘上执行命令,若超出棋盘,则命令执行失败,直接退出。
问:一开始应当将机器人摆在哪个位置,才能尽可能多地执行命令?
分析
我们可以 x , y x,y x,y两个维度分开看。
对 L , R L,R L,R。我们找到一个 x 0 x_0 x0,使得能执行最多的命令
对 U , D U,D U,D。我们找到一个 y 0 y_0 y0,使得能执行最多的命令
考虑 x x x轴。刚开始初始位置的选取范围可以是 [ 1 , m ] [1,m] [1,m],随着命令数的增加,这个选取范围也在不断地变小
具体选取缩小范围的方法见代码
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;
int n,m;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
for (int t=1;t<=T;++t)
{
string s;
cin>>n>>m;cin>>s;
int l = 1,r = m;
int cnt = 0;
for (int i=0;i<s.size();++i)
{
if (l==r)break;
int pre = cnt;
if (s[i]=='L')--cnt;
else if (s[i]=='R')++cnt;
else continue;
while (cnt+l<=0)++l;
while (cnt+r>m)--r;
}
int ll=1,rr = n;cnt=0;
for (int i=0;i<s.size();++i)
{
if (ll==rr)break;
int pre = cnt;
if (s[i]=='U')--cnt;
else if (s[i]=='D')++cnt;
else continue;
while (cnt+ll<=0)++ll;
while (cnt+rr>n)--rr;
}
cout<<ll<<" "<<l<<endl;
}
}
F.Robot on the Board 2
题目描述
与 E E E不同的是,指令被写在格子上,每次到一个格子,就会执行上面的指令。若超出棋盘,则指令执行失败。问从哪个格子开始,能执行最多的指令?
分析
可以看出,这类似于在一个有向图上求最长路。
但是这个图是有特点的!本图的原型是一个二维数组,这意味着每一个点的出度 ≤ 1 \le 1 ≤1
这意味着每个环都是单独的出现,即我们绝对不可能从一个环抵达到另一个环。
那么,我们的最远的路径一定是从一个入度为 0 0 0的点到达一个环然后绕环一周 或者 一个点
因此,我们 d f s dfs dfs每一个点,计算 p s [ i ] [ j ] ps[i][j] ps[i][j]表示从点 ( i , j ) (i,j) (i,j)开始走能到达的最远的路
如果该点在一个环上的话,那么 p s [ i ] [ j ] ps[i][j] ps[i][j]就为环的长度
空间很紧
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e6+100;
int xmp[1000],ymp[1000];
char gra[2005][2005];
int vis[2005][2005];
int ps[2005][2005];
int id[2005][2005];
int tot=0;
int n,m;
inline bool check(int x,int y)
{
return x>=1&&x<=n&&y>=1&&y<=m;
}
int dfs(int x,int y,int mk)
{
if (vis[x][y]==mk)
{
ps[x][y] = tot+1-id[x][y];
return ps[x][y];
}id[x][y] = ++tot;
if (vis[x][y]<mk&&vis[x][y]>0)return ps[x][y];
vis[x][y]=mk;ps[x][y]=1;
char ch = gra[x][y];
int nx = x + xmp[ch]; int ny = y + ymp[ch];
if (check(nx,ny))
{
dfs(nx,ny,mk);
if (id[nx][ny]<=id[x][y])
{
ps[x][y] = ps[nx][ny];
id[x][y] = id[nx][ny];
}
else ps[x][y] += ps[nx][ny];
}return ps[x][y];
}
int main()
{
ios::sync_with_stdio(0);
ymp['R'] = 1;ymp['L'] = -1;
xmp['U'] = -1;xmp['D'] = 1;
int T;cin>>T;
while (T--)
{
cin>>n>>m;for (int i=0;i<=n;++i)for (int j=0;j<=m;++j)vis[i][j] = ps[i][j] = 0;
for (int i=1;i<=n;++i)for (int j=1;j<=m;++j)cin>>gra[i][j];
int ansx = -1,ansy = -1,ans_len = 0;int mk =0;
for (int x=1;x<=n;++x)for (int y=1;y<=m;++y)if (vis[x][y]==0)
{
tot = 0;
int len = dfs(x,y,++mk);
if (len>ans_len)
{
ans_len = len;
ansx = x;
ansy = y;
}
}
cout<<ansx<<" "<<ansy<<" "<<ans_len<<"\n";
}
}
G.Banquet Preparations 1
题目描述
给定两个非负数组 a , b a, b a,b。以及一个非负整数m,保证 a i + b i ≥ m a_i + b_i ≥ m ai+bi≥m。
每个位置的两个数 a i , b i a_i,b_i ai,bi,需要减少 m m m。我们的目的是要使得平衡 ∣ ∑ a i − ∑ b i ∣ |\sum a_i - \sum b_i | ∣∑ai−∑bi∣尽量小。
这个并且要求出使得平衡值最小时,每个 a i , b i a_i,b_i ai,bi分别要减少多少
分析
重要的是一步转化
我们确定了,每一对都要减去 m m m。那么实际上最终 ∑ a i + b i \sum a_i+b_i ∑ai+bi 也是确定了的,记为 t o t tot tot
记 ∑ b i \sum b_i ∑bi为 s u m sum sum (这里的 b i b_i bi为修改过后的 b i b_i bi)
那么最终的答案就是 ∣ t o t − 2 ∗ s u m ∣ |tot - 2*sum| ∣tot−2∗sum∣
因此,我们只需考虑 s u m sum sum就可以了
s u m sum sum的目标最多两个:
- t o t tot tot为奇数时, t o t / 2 tot/2 tot/2或者 t o t / 2 + 1 tot/2+1 tot/2+1
- t o t tot tot为偶数时, t o t / 2 tot/2 tot/2
如何尽可能的优化 s u m sum sum呢?
首先对于每个 b i b_i bi记其最多可以减去 R [ i ] R[i] R[i],最少可以减去 L [ i ] L[i] L[i]
那么我们可以先减去 L [ i ] L[i] L[i],把此时的 s u m sum sum计算出来
然后,我们在根据现在的情况贪心的去减即可
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int a[maxn],b[maxn];
int L[maxn],R[maxn];int c[maxn];
int n,m;
ll sum,tot;
ll solve(ll tar)
{
static ll S;
S = sum;
for (int i=1;i<=n;++i)
{
ll cap = R[i] - L[i];
if (S<=tar)
{
c[i]=L[i];
}
else if (S-cap>=tar)
{
S-=cap;
c[i] = L[i]+cap;
}
else
{
c[i] = L[i]+S-tar;
S = tar;
}
}return abs(tot - 2*S);
}
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
cin>>n>>m;tot=0;sum=0;
for (int i=1;i<=n;++i)cin>>a[i]>>b[i];
for (int i=1;i<=n;++i)
{
tot += a[i]+b[i]-m;
L[i] = max(0,m-a[i]);
R[i] = min(b[i],m);
}
for (int i=1;i<=n;++i)sum += b[i]-L[i];
if (tot%2==0)
{
cout<<solve(tot>>1)<<endl;
for (int i=1;i<=n;++i)
{
cout<<m-c[i]<<" "<<c[i]<<endl;
}
}
else
{
ll ans = solve(tot>>1);
ll res = solve((tot>>1)+1);
if (ans<res)solve(tot>>1);
cout<<min(ans,res)<<endl;
for (int i=1;i<=n;++i)
{
cout<<m-c[i]<<" "<<c[i]<<endl;
}
}
}
}