Codeforces Round #679 (Div. 2, based on Technocup 2021 Elimination Round 1)
Codeforces Round #679 (Div. 2, based on Technocup 2021 Elimination Round 1) (传送)
A. Finding Sasuke
题意: t 组数据,每组数据 n 个数,第 i 个数记作$a_{i}$,保证 n 为偶数,且$|a_{i}|\leqslant 100$,现在让构造序列b,使得$\sum_{i=1}^{n}a_{i}\times b_{i}=0$且$|b_{i}|\leqslant 100$。输出序列b。
思路: n 为偶数,让序列 a 两个数为一组对应生成 b1 = a2 ,b2 = -1 × a1 即可。
代码:
#include<bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int t,n,x1,x2; cin>>t; while(t--){ cin>>n; for(int i=1;i<=n;i+=2){ cin>>x1>>x2; cout<<x2<<" "<<x1*(-1)<<" "; } cout<<endl; } return 0; }
B. A New Technique
题意:有一个 n × m 的数组,现在只记得每行从左往右的顺序而不记得具体是哪一行,记得每列从上往下的顺序但是不知道具体是哪一列,现在让根据所知道的信息还原之前的 n × m 的数组。数字1至nm每个只出现一次。
思路:因为每个数只出现一次,根据记得的每行从左往右的顺序可以确定每个数字位于第几列,根据记得的每列从上往下的顺序可以确定每个数字位于第几行,再根据得到的每个数字的行列位置还原出原二维数组。
代码:
#include<bits/stdc++.h> using namespace std; int ans[505][505]; int num_h[250005],num_l[250005]; int main() { int t,n,m,get_num; scanf("%d",&t); while(t--){ scanf("%d %d",&n,&m); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%d",&get_num); num_l[get_num]=j; } } for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ scanf("%d",&get_num); num_h[get_num]=j; } } for(int i=1;i<=n*m;i++){ ans[num_h[i]][num_l[i]]=i; } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ printf("%d ",ans[i][j]); } printf("\n"); } } return 0; }
C. Perform Easily
题意:有一个吉他6根弦,每根弦有一个根音,根音+吉他的格数为它能发出的音符,问弹奏所有的 n 种音符所需吉他格子的最小跨度为多少。
思路:排序后双指针并用线段树维护。首先我们定义一个结构体,分别存储第 i 个音符和能发出第 i 个音符在吉他上需要按住的位置,那么这个结构体一共为 6n 个元素,对这 6n 个元素按照在吉他上的位置从小到大进行排序。排序之后采用双指针,两个指针对应元素的变量差为我们在吉他上需要移动的长度。当我们还未弹奏完所有种类的音符,右指针一直加。当右指针满足后,左指针循环一直到不满足弹奏完所有种类的音符。期间一直更新答案。线段树用来维护我们当前弹奏了多少种类的音符。
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; int a[7],b[maxn],seg_tree[maxn<<2],music[maxn],ans=1e9+7; struct node{//loc存该种音符对应在吉他上的位置,ii存弹奏的是哪一种音符 int loc,ii; }; node lis[6*maxn]; void update(int l,int r,int node,int pos,int val){ if(l==r){ seg_tree[node]=val; return; } int mid=(l+r)>>1; if(pos<=mid) update(l,mid,node<<1,pos,val); else update(mid+1,r,node<<1|1,pos,val); seg_tree[node]=seg_tree[node<<1]+seg_tree[node<<1|1]; } int query(int ql,int qr,int node,int l,int r) { if(r<ql || l>qr) return 0; if(l>=ql && r<=qr) return seg_tree[node]; int mid=(l+r)>>1; int x=query(ql,qr,node<<1,l,mid); int y=query(ql,qr,node<<1|1,mid+1,r); return x+y; } int main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n; for(int i=1;i<=6;i++) { cin>>a[i]; } cin>>n; for(int i=1,ct=1;i<=n;i++) { cin>>b[i]; for(int j=1;j<=6;j++,ct++){ lis[ct].ii=i; lis[ct].loc=b[i]-a[j]; } } sort(lis+1,lis+1+6*n,[](const node &x,const node &y){return x.loc<y.loc;}); int l=1,r=0; while(r+1<=6*n){ while(r+1<=6*n && query(1,n,1,1,n)<n){//如果当前未弹奏所有种类音符,则右指针继续扫描 r++; if(music[lis[r].ii]==0){//如果新弹奏的为未弹奏的新音符 update(1,n,1,lis[r].ii,1);//更新线段树 } music[lis[r].ii]++;//该种音符计数加一 } while(l<=r && query(1,n,1,1,n)==n){//如果当前已弹奏所有种类音符,则左指针继续扫描 ans=min(ans,lis[r].loc-lis[l].loc);//更新答案 if(music[lis[l].ii]==1){//如果删除 l 对应的音符则导致所选段无法弹奏该种音符 update(1,n,1,lis[l].ii,0);//更新线段树 } music[lis[l].ii]--;//该种音符计数减一 l++; } } cout<<ans<<endl; return 0; }
D. Shurikens
题意:一个武器店,进行上架和出售,顾客来购买时会先选择目前最便宜的进行购买,店主根据记忆记录了一份上架和出售记录,"+" 为上架,"- x" 为按照 x 的价格出售了一份武器。现在问店主的这份记录是否合法,若合法,给出一种店主上架武器的顺序,按照上架时的价格输出。
思路:首先需要注意过程中注意判断 + 和 - 是否匹配,不能没有商品时出售。接下来用栈来维护当前可以出售的最低价格为 x 的武器有多少个。 (讲解都写成注释加在了代码中。)
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int weapon[maxn];//存放答案 stack<int>st;//存储压入的武器的编号 stack<pair<int,int> >limit;//存储当前可以出售的最低价格为first的武器有second个 int main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); bool flag=true; int n,get_num,num,last; string s; cin>>n; for(int i=1,j=0,ct=0,wp=0;i<=2*n;i++){//j用来进行+-匹配,ct记录有的多少个连续+,wp记录第几个+ cin>>s; if(s=="+"){ j++;ct++;wp++; st.push(wp);//压入第wp号武器 }else if(s=="-"){ cin>>get_num; j--; if(j<0){//如果+和-不匹配,则该记录错误,直接退出 flag=false;break; } if(ct==0){//判断上一次是否为出售,ct=0则为上次也是出售 if(get_num<limit.top().first){//如果当前出售的武器价格小于了可以出售的价格最小值 flag=false;break;//该记录错误,直接退出 }else{ weapon[st.top()]=get_num;//记录武器价格 st.pop();//弹出已记录武器 last=limit.top().second-1;//当前可以出售的最低价的武器减少1个 num=max(limit.top().first,get_num);//更改价格限制,之后出售的武器不能更低 limit.pop();//弹出限制 if(last>0) limit.push(make_pair(num,last));//将改好的限制压入 } }else{//若上次不为出售 weapon[st.top()]=get_num;//可以假设上一次上架的武器为当前出售的武器,记录价格 st.pop();//弹出已记录武器 ct--;//连续的武器减一(刚弹出的武器) if(ct>0)limit.push(make_pair(get_num,ct));//当前可以出售的最低格为get_num的武器有ct个 ct=0;//连续+被打断,计数清零 } } } if(flag){ cout<<"YES"<<endl; for(int i=1;i<=n;i++){ cout<<weapon[i]<<" "; } cout<<endl; }else{ cout<<"NO"<<endl; } return 0; }