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 = a,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;
}
View Code

 

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;
}
View Code

 

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;
}
View Code

 

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;
}
View Code

 

posted @ 2020-10-26 00:10  blacktion  阅读(308)  评论(0编辑  收藏  举报