最近比赛好多啊,打不完了。这场的题比较骗人,真实难度实际上不高,就是想不到乐 。
A 小红的单词整理
code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
string s1,s2;
int main(){
cin>>s1>>s2;
cout<<s2<<endl<<s1;
return 0;
}
B 小红煮汤圆
思路:
算出前 i i i 次煮汤圆总共需要拆开的袋数,再算出前 i − 1 i-1 i−1 煮汤圆总共需要拆开的袋数,它们的差值就是第 i i i 次需要拆开的袋数。
前 i i i 次煮汤圆需要煮汤圆需要 i ∗ k i*k i∗k 个,假设最少要拆了 t t t 袋,那么会拆出来 t ∗ x t*x t∗x 个汤圆。根据定义可以得出不等式: ( t − 1 ) ∗ x < i ∗ k ≤ t ∗ x (t-1)*x\lt i*k\le t*x (t−1)∗x<i∗k≤t∗x t − 1 < i ∗ k x ≤ t t-1\lt \frac {i*k}x \le t t−1<xi∗k≤t t = ⌈ i ∗ k x ⌉ t=\left\lceil \frac {i*k}x\right\rceil t=⌈xi∗k⌉
code:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n,x,k;
int main(){
cin>>n>>x>>k;
int m;
cout<<(m=n*x/k)<<endl;
for(int i=1,t1,t2;i<=m;i++){
t1=(i*k+x-1)/x;
t2=((i-1)*k+x-1)/x;
cout<<t1-t2<<" ";
}
return 0;
}
C 小红的 01 串
思路:
考虑如何删除,如果前面有前导零肯定删掉,如果没有 1 1 1 就是把串删完,结果就是0。有 1 1 1 就一定会遇到 1 1 1。我们会保留这个 1 1 1 之后只用操作 2 2 2 删除第二个字符,这时可以把第一个字符直接忽略,我们的操作 2 2 2 相当于是在删除剩余字符串的第一个字符。所以我们可以先看一下串里有没有 1 1 1,没有就返回 0 0 0,否则把第一个 1 1 1 之前的部分删掉。之后再在剩余的字符串上不停删除第一个字符看 01 01 01 差值的最大值是多少。
可以算出 01 01 01 差值的前缀和 s s s,然后用 s [ n ] − m i n { s [ i ] } s[n]-min\{s[i]\} s[n]−min{s[i]} 就行了。
#include <iostream>
#include <cstdio>
#include <cstdio>
using namespace std;
const int maxn=1e5+5;
int n,s[maxn];
string str;
int main(){
cin>>str;
if(str.find_first_of("1")==string::npos){
cout<<0<<endl;
return 0;
}
str=" "+str.substr(str.find_first_of("1")+1);
n=str.length()-1;
int minn=0;
for(int i=1;i<=n;i++){
s[i]=s[i-1]+((str[i]=='0')?-1:1);
minn=min(minn,s[i]);
}
cout<<s[n]-minn+1<<endl;
return 0;
}
D 小红的数组清空
思路:
由于元素可以重复,所以不能排序后找公差为 1 1 1 递增的等差数列。
这个题直接模拟删数过程:先删掉剩余数中最小的数 x x x,如果存在一个 x + 1 x+1 x+1,就顺便删掉,再找 x + 2 x+2 x+2,类推,没有就重新选数删掉。
code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
const int maxn=1e5+5;
int n;
map<int,int> mp;
int main(){
cin>>n;
for(int i=1,t;i<=n;i++){
cin>>t;
mp[t]++;
}
int ans=0;
while(!mp.empty()){
auto it=mp.begin();
int x=it->first;
ans++;
if(--it->second==0)mp.erase(it);
while(mp.count(x+1)){
x++;
mp[x]--;
if(mp[x]==0)mp.erase(x);
}
}
cout<<ans;
return 0;
}
E 小红勇闯地下城
思路:
一眼看上去像DP,但是起点和终点不在角上,而且移动可以绕圈。
考虑到我们到达某个位置的时候,一定是血量更多是更好的,而不关心我们具体是怎么到达这个位置的。而我们的血量是不断减少的,中间不会回血。这意味着如果我们从所有状态中选出血量最多的状态,如果这个状态所在位置之前还没有被访问过,那么现在这个状态的血量就是最优的,我的意思是,之后到达这个位置的最大血量就再也不可能被刷新了。
证明是显然的:现在所有状态的血量都少于这个状态,我们的血量还是不断减少的,无论从什么状态来推,之后到达的时候血量一定不可能大于现在的血量了。
这有堆优化dijkstra那味了。所以我们这里使用堆来代替队列进行BFS(或者说是堆优化dijkstra也没有问题),某个位置的最大血量不可能被刷新就代表这个位置之后就不可能入堆了,所以我们相当于每从堆中取出一个新点,就确定好了一个点。因为每个点都只会拿来拓展一次,拓展一次要检查四个点,所以总的时间复杂度大概是 O ( 4 n ∗ m ∗ l o g ( n ∗ m ) ) O(4n*m*log(n*m)) O(4n∗m∗log(n∗m))。
需要动态开空间。
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
int T,n,m,hp;
struct state{
int x,y,hp;
bool operator<(const state x)const{return hp<x.hp;}
state(int x,int y,int hp):x(x),y(y),hp(hp){};
};
int fx[]={1,-1,0,0},fy[]={0,0,1,-1};
int main(){
cin>>T;
while(T--){
cin>>n>>m>>hp;
vector<vector<int> > vis(n+1,vector<int>(m+1,0));//到达(x,y)位置的最大血量
vector<vector<int> > mp(n+1,vector<int>(m+1));
priority_queue<state> q;
int sx,sy,ex,ey;
for(int i=1;i<=n;i++){
string s;
cin>>s;
s=" "+s;
for(int j=1;j<=m;j++){
if(s[j]=='S'){
sx=i;
sy=j;
mp[i][j]=0;
}
else if(s[j]=='T'){
ex=i;
ey=j;
mp[i][j]=0;
}
else mp[i][j]=s[j]-'0';
}
}
bool flag=false;
q.push(state(sx,sy,hp));
vis[sx][sy]=hp;
while(!q.empty()){
int ux=q.top().x,uy=q.top().y,hp=q.top().hp;
q.pop();
if(hp>vis[ux][uy])continue;
// printf("(%d,%d) hp=%d\n",ux,uy,hp);
if(ux==ex && uy==ey){
flag=true;
break;
}
for(int i=0,x,y;i<4;i++){
x=ux+fx[i];
y=uy+fy[i];
if(x<1 || x>n || y<1 || y>m)continue;
if(hp-mp[x][y]>vis[x][y]){
vis[x][y]=hp-mp[x][y];
q.push(state(x,y,vis[x][y]));
}
}
}
puts((flag)?"Yes":"No");
}
return 0;
}
F 小红的数组操作
思路:
这题相当于单调栈维护的贪心。具体可以看这个大佬的讲解,有点忙就不多赘述了。
听说出题人说是斜率优化,但是直播录播和题解我都没看到,等有了再研究研究,如果有会的可以踢我一脚。
code:
#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
const ll inf=1e9;
int n;
ll a[maxn],s[maxn],nw[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]+=inf;
a[i]-=i;
s[i]=s[i-1]+a[i];
}
stack<pair<ll,int> > s;
for(int i=1;i<=n;i++){
pair<ll,int> x(a[i],1),t;
ll tot=a[i],len=1,ave=a[i],t1,t2;
while(!s.empty() && s.top().first>=ave){
t=s.top();
s.pop();
tot+=t.first*t.second;
len+=t.second;
ave=tot/len;
}
t2=tot-ave*len;
t1=len-t2;
s.push(make_pair(ave,t1));
if(t2)s.push(make_pair(ave+1,t2));
}
int p=n;
while(!s.empty()){
for(;s.top().second;s.top().second--){
nw[p--]=s.top().first;
}
s.pop();
}
ll ans=0;
for(ll i=1,dt;i<n;i++){
if(a[i]>nw[i]){
dt=a[i]-nw[i];
a[i]-=dt;
a[i+1]+=dt;
ans+=dt;
}
}
for(int i=1;i<=n;i++)
nw[i]+=i-inf;
cout<<ans<<endl;
for(int i=1;i<=n;i++)
cout<<nw[i]<<" ";
return 0;
}