修養身心打水題 Div 3 后面幾題還是有點難度的
比賽鏈接
A. Linear Keyboard
難度: \(800\) (巨水)
題目大意:
給你一個按鍵打亂只有一行的鍵盤,你可以初始在任意位置,若第 \(i\) 個鍵在位置 \(x_i\) ,下個鍵 \(j\) 在 \(x_j\) 則問你需要 \(|x_i-x_j|\) 的時間來移動 ,輸出打出所給序列的所需時間。
思路: 按照題目模擬即可,時間復雜度\(O(TS)\)
代碼:
#include<bits/stdc++.h>
using namespace std;
const int N=55;
int t;
int str[30];
char ch[N];
inline void Read()
{
for(int i=1;i<=26;i++)
{
char ch=getchar();
while(!islower(ch)) ch=getchar();
str[ch-'a']=i;
}
}
inline void Work()
{
int ans=0;
scanf("%s",ch+1);
for(int i=2;ch[i];i++)
ans+=abs(str[ch[i]-'a']-str[ch[i-1]-'a']);
printf("%d\n",ans);
}
int main()
{
scanf("%d",&t);
while(t--)
{
Read();
Work();
}
return 0;
}
B. Odd Grasshopper
難度: \(900\) (沒那么水)
題目大意:
一條無限長的數軸上有一個點,一開始在 \(x_0\) 處。它會移動 \(n\) 次,第 \(i\) 次它會按照以下規則移動:
- 如果它當前所處的位置上的數 \(x\) 是奇數,則它會移動到 \(x+i\) ,否則它會移動到 \(x-i\)。
求移動 \(n\) 次后該點所處的位置上的數。
思路:
按照題目模擬可以發現每過 \(4\) 步都會回到原點,而其他步數與出發點的奇偶有關
設出發點為奇數時與出發點相對位置為\(f_1()\) 偶數時為 \(f_2()\) 可以推出
時間復雜度\(O(T)\)
代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
ll st,n;
inline void Read()
{
scanf("%lld%lld",&st,&n);
}
inline void Work()
{
if(st&1)
{
if(n%4==0) printf("%lld\n",st);
if(n%4==1) printf("%lld\n",st+n);
if(n%4==2) printf("%lld\n",st-1);
if(n%4==3) printf("%lld\n",st-n-1);
}
else
{
if(n%4==0) printf("%lld\n",st);
if(n%4==1) printf("%lld\n",st-n);
if(n%4==2) printf("%lld\n",st+1);
if(n%4==3) printf("%lld\n",st+n+1);
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
Read();
Work();
}
return 0;
}
C. Minimum Extraction
難度: \(1000\) (似乎比 \(B\) 題還水 ?)
題目大意:
給你一個序列 ,每次可以選擇操作將序列中最小的一個數取出,再將序列剩下的所有數加上它。
再若干次操作后可以使序列中的最小值最大,請求出這個最大的最小值
思路:
排序后按照題目模擬並且動態更新答案即可,時間復雜度\(O(nlogn)\)
代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
const ll INF=0X3f3f3f3f3f3f3f3f;
int t,n;
int a[N];
ll ans,res;
inline void Read()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
}
inline void Work()
{
ans=-INF,res=0;
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
ans=max(ans,a[i]-res);
res=a[i];
}
printf("%lld\n",ans);
}
int main()
{
scanf("%d",&t);
while(t--)
{
Read();
Work();
}
return 0;
}
D. Blue-Red Permutation
難度: \(1300\) (淼)
題目大意:
給你一個序列 ,序列中每個數有紅色和藍色兩種 ,你可以將顏色為紅色的數減掉任意值,顏色為藍色的數加上任意值
問若干次操作后可以使序列中的值為 \(1 \sim n\)
思路:
對於紅色的數和藍色的數分別從小到大處理,根據貪心思想:當目前需要的值過大時,藍色的數就沒有用了,所以對於一個所需要的數,優先使用藍色的,再用紅色的,如果都不能用了,就無解了。時間復雜度\(O(nlogn)\)
代碼:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int t,n;
int a[N];
multiset<int> blue,red;
inline void Read()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
blue.clear(),red.clear();
for(int i=1;i<=n;i++)
{
char ch;
do ch=getchar();
while(!isupper(ch));
if(ch=='B') blue.insert(a[i]);
else red.insert(a[i]);
}
}
inline void Work()
{
for(int i=1;i<=n;i++)
{
auto it=blue.begin(),jt=red.begin();
if(it!=blue.end()&&*it>=i) blue.erase(it);
else if(jt!=red.end()&&*jt<=i) red.erase(jt);
else return (void)puts("NO");
}
puts("YES");
}
int main()
{
scanf("%d",&t);
while(t--)
{
Read();
Work();
}
return 0;
}
E. Robot on the Board 1
難度: \(1600\) (淼)
題目大意:
有一個機器人在 \(n \times m\) 的棋盤上行走(每次只能上下左右移動),給你一個操作序列 \(s\) ,問你機器人從哪開始可以完成最多的操作而不超過邊界。
思路:
由於棋盤是矩形的特性,我們只用管機器人路徑的上下距離極差與左右距離極差即可。直接模擬記錄答案並且在不合法時跳出即可。時間復雜度\(O(nm)\)
代碼:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int INF=0X3f3f3f3f;
int t;
int n,m;
char str[N];
int minx,miny,maxx,maxy,ansx,ansy;
inline void Read()
{
scanf("%d%d",&n,&m);
scanf("%s",str+1);
}
inline void Work()
{
int x=0,y=0;
ansx=ansy=1,minx=maxx=miny=maxy=0;
for(int i=1;str[i];i++)
{
if(str[i]=='U') x--;
if(str[i]=='D') x++;
if(str[i]=='L') y--;
if(str[i]=='R') y++;
minx=min(minx,x),miny=min(miny,y);
maxx=max(maxx,x),maxy=max(maxy,y);
if(maxx-minx>=n||maxy-miny>=m) break;
ansx=-minx+1,ansy=-miny+1;
}
printf("%d %d\n",ansx,ansy);
}
int main()
{
scanf("%d",&t);
while(t--)
{
Read();
Work();
}
return 0;
}
F. Robot on the Board 2
難度: \(2300\) (需要動腦)
題目大意:
還是那個機器人,在 \(n \times m\) 的棋盤上行走(每次只能上下左右移動),每個格子都有一個固定操作,機器人除了不能超過邊界外還不能走到走過的格子上,問從哪出發可以最大化走過的路徑長
思路:
由於每個格子上的操作都是固定的,我們可以得知當我們到了一個格子上時有會有一個固定的結局,掉出邊界 \(or\) 走到環上。
所以我們可以考慮記憶化搜索,記錄每個格子到自己的終點的距離。注意環上的點的答案要全部設定為環長(顯然)。
時間復雜度\(O(nm)\)
代碼:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=2010;
int t,n,m;
char str[N][N];
int ansx,ansy,ans;
int d[N][N];
vector<PII> path;
inline bool Check(int x,int y)
{
return x>0&&y>0&&x<=n&&y<=m;
}
inline void Read()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",str[i]+1);
}
inline void Work()
{
ans=ansx=ansy=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(d[i][j]>0) continue;//記憶化
int nowx=i,nowy=j,len=0;
while(Check(nowx,nowy)&&d[nowx][nowy]==0)//直接進行搜索
{
d[nowx][nowy]=--len;
path.push_back({nowx,nowy});
if(str[nowx][nowy]=='L') nowy--;
else if(str[nowx][nowy]=='R') nowy++;
else if(str[nowx][nowy]=='U') nowx--;
else if(str[nowx][nowy]=='D') nowx++;
}
int cnt=1;
if(Check(nowx,nowy))//兩種情況:在環上或已經訪問了
{
if(d[nowx][nowy]<0)//如果最后是走到環上
{
int circle_len=d[nowx][nowy]-len+1;
for(int i=1;i<=circle_len;i++)
{
auto pos=path.back();
path.pop_back();
d[pos.first][pos.second]=circle_len;
}
}
cnt=d[nowx][nowy]+1;
}
while(path.size())//處理除環外的路徑
{
auto pos=path.back();
path.pop_back();
d[pos.first][pos.second]=cnt++;
}
if(d[i][j]>ans)
{
ans=d[i][j];
ansx=i,ansy=j;
}
}
printf("%d %d %d\n",ansx,ansy,ans);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
d[i][j]=0;//清空本次的查詢
}
int main()
{
scanf("%d",&t);
while(t--)
{
Read();
Work();
}
return 0;
}
G. Banquet Preparations 1
難度: \(2200\) (需要動腦)
題目大意:
一共有 \(n\) 道菜,每道菜吃 \(m\) kg 每道菜有 \(a_i\) kg 的魚肉 \(b_i\) kg 的肉,設每道吃 \(x_i\) 的魚肉,試最小化
思路:
真閱讀理解題
試着化簡式子可得
對於式子的左半部分是個定值我們記為 \(sum\) ,式子的右邊是一堆范圍的和 ,其中每個 \(x_i\) 滿足
所以我們可以設
接下來分類討論
- 若 \(sum \leq 2L\) 將所有值取到最小
- 若 \(sum \leq 2R\) 將所有值取到最大
- 否則先都取最小值,在逐步考慮,能取大就取大即可(具體見代碼)
時間復雜度\(O(n)\)
代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=2e5+10;
int n,m,t;
ll sum,l,r;
int ans[N];
PLL dish[N],range[N];
inline void Read()
{
scanf("%d%d",&n,&m);
sum=l=r=0;
for(int i=1;i<=n;i++)
{
static int a,b;
scanf("%d%d",&a,&b);
sum+=a-b+m;
range[i].first=max(m-b,0),range[i].second=min(m,a);
l+=range[i].first,r+=range[i].second;
}
}
inline void Work()
{
if(2*r<=sum)//開到最大
{
printf("%lld\n",sum-2*r);
for(int i=1;i<=n;i++)
printf("%lld %lld\n",range[i].second,m-range[i].second);
}
else if(sum<=2*l)//最小
{
printf("%lld\n",2*l-sum);
for(int i=1;i<=n;i++)
printf("%lld %lld\n",range[i].first,m-range[i].first);
}
else//貪心分配
{
printf("%lld\n",sum%2);
ll base=(sum>>1)-l;
for(int i=1;i<=n;i++)
{
ans[i]=range[i].first;
int delta=range[i].second-range[i].first;
if(base>=delta)
base-=delta,ans[i]+=delta;
else ans[i]+=base,base=0;
}
for(int i=1;i<=n;i++)
printf("%d %d\n",ans[i],m-ans[i]);
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
Read();
Work();
}
return 0;
}
H. Banquet Preparations 2
難度: \(2200\) (需要動腦)
題目大意:
一共有 \(n\) 道菜,每道菜吃 \(m_i\) kg,每道菜有 \(a_i\) kg 的魚肉 \(b_i\) kg 的肉,設每道吃 \(x_i\) 的魚肉,試最小化剩下的不同菜
- 兩道菜 \(i\),\(j\)相同當且僅當兩道菜 \(a_i=a_j \wedge b_i=b_j\)
思路:
我們可以得到兩道菜可能相同必須要滿足 \(a_i+b_i-m_i=a_j+b_j-m_j\) ,所以我們可以按照這個值給所有的菜分類。
分類之后設吃完后的 \(a_i\) 為 \(x_i\) 則
令式子左邊為 \(l_i\) 式子右邊為 \(r_i\)
所以我們把每類的按 \(r_i\) 為第一關鍵字排序,\(l_i\) 為第二關鍵字排序,用單指針貪心維護右端點即可。
時間復雜度\(O(nlogn)\)
代碼:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=2e5+10;
int T,n,res;
struct node
{
int a,b,m;
int l,r,sum,id;
inline bool operator<(const node &t) const
{
if(sum!=t.sum) return sum<t.sum;
if(r!=t.r) return r<t.r;
return l<t.l;
}
}seg[N];
PII ans[N];
inline void Read()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&seg[i].a,&seg[i].b,&seg[i].m);
seg[i].sum=seg[i].a+seg[i].b-seg[i].m;
seg[i].l=max(0,seg[i].a-seg[i].m),seg[i].r=seg[i].a+min(seg[i].b-seg[i].m,0);
seg[i].id=i;
}
}
inline void Work()
{
res=0;
sort(seg+1,seg+n+1);
int maxr=-1;
for(int i=1;i<=n;i++)
{
if(seg[i].sum!=seg[i-1].sum||seg[i].l>maxr)
maxr=seg[i].r,res++;
ans[seg[i].id].first=seg[i].a-maxr;
ans[seg[i].id].second=seg[i].m-ans[seg[i].id].first;
}
printf("%d\n",res);
for(int i=1;i<=n;i++)
printf("%d %d\n",ans[i].first,ans[i].second);
}
int main()
{
scanf("%d",&T);
while(T--)
{
Read();
Work();
}
return 0;
}
