第十五届中北大学算法与程序设计竞赛(公开赛)题解

比赛链接

A.俄罗斯方块

题意:
10∗10的图
给四种俄罗斯的图形,给出位置横坐标,求下降完后的图形
题解:
图很小,直接从上向下模拟下降过程即可
AC代码

#include
using namespace std;
int v[12][12];
void solve(){
    int n;cin>>n;
    for(int i=1;i<=11;i++)v[11][i]=1;
    while(n--){
        int x,y;cin>>x>>y;
        if(x==1){
            for(int i=2;i<=11;i++){
                if(!(!v[i][y]&&!v[i][y+1]&&!v[i-1][y]&&!v[i-1][y+1])){
                    i--;
                    v[i][y]=1,v[i][y+1]=1,v[i-1][y]=1,v[i-1][y+1]=1;
                    break;
                }
            }
        }
        if(x==2){
            for(int i=2;i<=11;i++){
                if(!(!v[i][y]&&!v[i][y+1]&&!v[i][y+2]&&!v[i-1][y])){
                    i--;
                    v[i][y]=1,v[i][y+1]=1,v[i][y+2]=1,v[i-1][y]=1;
                    break;
                }
            }
        }
        if(x==3){
            for(int i=1;i<=11;i++){
                if(!(!v[i][y]&&!v[i][y+1]&&!v[i][y+2]&&!v[i][y+3])){
                    i--;
                    v[i][y]=1,v[i][y+1]=1,v[i][y+2]=1,v[i][y+3]=1;
                    break;
                }
            }
        }
        if(x==4) {
            for(int i=2;i<=11;i++){
                if(!(!v[i][y]&&!v[i][y+1]&&!v[i][y+2]&&!v[i-1][y+1])){
                    i--;
                    v[i][y]=1,v[i][y+1]=1,v[i][y+2]=1,v[i-1][y+1]=1;
                    break;
                }
            }
        }
    }
    for(int i=1;i<=10;i++){
        for(int j=1;j<=10;j++)cout<<v[i][j]<<' ';
        cout<<endl;
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    //int t;cin>>t;
    //while(t--)solve(),cout<<'\n';
    solve();
    return 0;
}

B.真的是签到题

直接输出即可
AC代码

#include
using namespace std;
void solve(){
    for(int i=0;i<3;i++)cout<<"NUC2020!!!"<<endl;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

C.港口

题意:
给定一个长度为 n 的数列 a1,a2,…,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。问变相等的最少操作次数。
题解:
想要得到所有数都一样,也就是要使差分序列的 2~n 项全为 0。而第 1 项代表第一个数的大小,改变第1项只会让数列整体变大或变小,并不影响数列相同。计算差分序列,记录差分序列中正数和负数的个数,每次操作可以让差分序列中的两个数一个 +1 另一个 -1,因此,也就是差分后 Max( 正数和,负数和 ) 次。
AC代码

#include
using namespace std;
#define fi first
#define se second
#define lowbit(x) x&(-x)
#define pb push_back
#define iter set::iterator

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll, ll> pll;

const int N = 1e6+5;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const int exdir[4][2]={1,1,1,-1,-1,1,-1,-1};

ll qpow(ll x,ll y){
    ll ans=1,t=x;
    while(y>0){
        if(y&1)ans*=t,ans%=mod;
        t*=t,t%=mod;
        y>>=1;
    }
    return ans%mod;
}
ll a[N],p,q;
void solve(){
    int n;cin>>n;
	for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=2;i<=n;i++){
        if(a[i]-a[i-1]>0)p+=a[i]-a[i -1];
        else q-=a[i]-a[i-1];
    }
    cout<<max(p,q);
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    //int t;cin>>t;
    //while(t--)solve(),cout<<'\n';
    solve();
    return 0;
}


D.构造数组

题意
构造一个长度为n的数组A,构造方式如下:
依次进行n次操作,第i次操作在数组A的index[i]位置处插入整数number[i].
最后从左到右输出数组A的元素
题解:
这道题从前向后考虑是不行的,我们从后向前考虑,最后一个插入的数位置肯定是index[n],然后倒二插入的位置可能是index[n-1]或index[n-1]+1,显然最后一个数的插入影响的倒二的数的位置,其实就是后面插入的数对前面插入的数的位置产生后移,所以每次只要找x-sum(x)=index[i]就行了,最终位置就是x,用树状数组维护前和就好了。
AC代码

#pragma GCC optimize(2)
#include
using namespace std;
#define fi first
#define se second
#define lowbit(x) x&(-x)
#define pb push_back
#define iter set::iterator

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll, ll> pll;

const int N = 1e6+5;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const int exdir[4][2]={1,1,1,-1,-1,1,-1,-1};

ll qpow(ll x,ll y){
    ll ans=1,t=x;
    while(y>0){
        if(y&1)ans*=t,ans%=mod;
        t*=t,t%=mod;
        y>>=1;
    }
    return ans%mod;
}
ll a[N],b[N],ans[N],tree[N],n;
void add(int x,int y){
    while(x<=n){
        tree[x]+=y;
        x+=lowbit(x);
    }
}
ll sum(int x){
    ll sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
int find(int x){
    int l=1,r=n;
    while(l<r){
        int mid=(l+r)>>1;
        if(mid-sum(mid)<x)l=mid+1;
        else r=mid;
    }
    return l;
}
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i]>>b[i];
	for(int i=n;i>0;i--){
        int x=a[i];
        int k=find(x);
        add(k,1);
        ans[k]=b[i];
	}
	for(int i=1;i<=n;i++)cout<<ans[i]<<' ';
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    //int t;cin>>t;
    //while(t--)solve(),cout<<'\n';
    solve();
    return 0;
}

E.简单的线性代数*

题意:
已知A是一个n阶方阵,且Ak=0,E是一个n阶单位矩阵,求(E+A+……+A(k-1))的逆
题解
我们用等比数列求和得到 E+A+……+A(k-1)=(E-Ak)/(E-A),
(E-Ak)/(E-A)*B=E,又Ak=0,则B=(E-A)
AC代码

#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define lowbit(x) x&(-x)

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll, ll> pll;

const int N = 1e5+5;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double esp=1e-8;
const double PI=acos(-1.0);
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const int exdir[4][2]={1,1,1,-1,-1,1,-1,-1};

ll qpow(ll x,ll y){
    ll ans=1,t=x;
    while(y>0){
        if(y&1)ans*=t,ans%=mod;
        t*=t,t%=mod;
        y>>=1;
    }
    return ans%mod;
}
int a[1001][1001];
void solve(){
    ll n,k;cin>>n>>k;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
            if(i==j)a[i][j]=1-a[i][j];
            else a[i][j]*=-1;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)cout<<a[i][j]<<' ';
        cout<<'\n';
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    //int t;cin>>t;
    //while(t--)solve(),cout<<'\n';
    solve();
    return 0;
}

F.集合操作

题意
有一个集合S,刚开始是空集,现在有3种操作。
1.往集合中添加x,如果集合中已经存在x,则不添加。
2.从集合中删除x,如果集合中不存在x,则不删除。
3.查询大于等于x且不在集合S中的最小的正整数
题解
先离散化,然后用树状数组维护一下前缀,查询的时候我们二分找出一个数与x的实际差刚好等于与x间隔的数(集合中存在)的个数,也就是

sum(mid)-sum(k-1)==c[mid]-c[k]+1//k是离散化后x的下标,c是离散数组

AC代码

#include
using namespace std;
#define fi first
#define se second
#define lowbit(x) x&(-x)
#define pb push_back
#define iter set::iterator

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll, ll> pll;

const int N = 1e6+5;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const int exdir[4][2]={1,1,1,-1,-1,1,-1,-1};

ll qpow(ll x,ll y){
    ll ans=1,t=x;
    while(y>0){
        if(y&1)ans*=t,ans%=mod;
        t*=t,t%=mod;
        y>>=1;
    }
    return ans%mod;
}
ll a[N],b[N],c[N],tree[N],v[N],len;
void add(int x,int y){
    while(x<=len){
        tree[x]+=y;
        x+=lowbit(x);
    }
}
ll sum(int x){
    ll sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
void solve(){
	int n;cin>>n;
	for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i];
        c[i]=b[i];
	}
	sort(c+1,c+1+n);
	len=unique(c+1,c+1+n)-c-1;
	c[len+1]=INF;
	for(int i=1;i<=n;i++){
        int x=a[i],y=b[i];
        int k=lower_bound(c+1,c+1+len,y)-c;
        if(x==1&&!v[k])v[k]=1,add(k,1);
        if(x==2&&v[k])v[k]=0,add(k,-1);
        if(x==3){
            int l=k,r=len+1;
            while(l<r){
                int m=(r+l)>>1;
                if(sum(m)-sum(k-1)==c[m]-c[k]+1)l=m+1;
                else r=m;
            }
            if(l==k)cout<<c[k]<<'\n';
            else cout<<c[l-1]+1<<'\n';
        }
	}
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    //int t;cin>>t;
    //while(t--)solve(),cout<<'\n';
    solve();
    return 0;
}

G.数位操作1

题意:
给你一个正整数 n ,找到最小的某个数据 ans (ans >9)
要求 ans 的每一数位(个位 十位 百位 千位…) 乘积与n相等, 不存在输出-1
题解:
按贪心思想,将原数按9~1分解即可,如果最后剩下的数大于9,则说明不存在,注意特判1.
AC代码

#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define lowbit(x) x&(-x)

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll, ll> pll;

const int N = 1e5+5;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double esp=1e-8;
const double PI=acos(-1.0);
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const int exdir[4][2]={1,1,1,-1,-1,1,-1,-1};

ll qpow(ll x,ll y){
    ll ans=1,t=x;
    while(y>0){
        if(y&1)ans*=t,ans%=mod;
        t*=t,t%=mod;
        y>>=1;
    }
    return ans%mod;
}

void solve(){
    ll n;
    while(cin>>n){
        if(n==1){cout<<11<<endl;continue;}
        vector<int>v;
        for(ll i=9;n>1&&i>1;i--){
            while(n%i==0)n/=i,v.pb(i);
        }
        if(n>9){cout<<-1<<endl;continue;}
        else if(n>1)v.pb(n);
        if(v.size()==1)v.pb(1);
        sort(v.begin(),v.end());
        for(auto i:v)cout<<i;cout<<endl;
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    //int t;cin>>t;
    //while(t--)solve(),cout<<'\n';
    solve();
    return 0;
}

K.签个到

题意
给n个数,进行m次操作
每次操作可以使得任意一个数加或减他的下标,问m次操作后这个数列的极差最大是多少
题解
排一下序,然后对于任意一个数进行m次操作后最大极差为

max(abs(a[i]-b[1]),abs(a[i]-b[n]))+i*m

显然枚举一下a[n],找对各个数的操作极差即可
AC代码

#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define lowbit(x) x&(-x)
 
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll, ll> pll;
 
const int N = 1e5+5;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double esp=1e-8;
const double PI=acos(-1.0);
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const int exdir[4][2]={1,1,1,-1,-1,1,-1,-1};
 
ll qpow(ll x,ll y){
    ll ans=1,t=x;
    while(y>0){
        if(y&1)ans*=t,ans%=mod;
        t*=t,t%=mod;
        y>>=1;
    }
    return ans%mod;
}
ll a[N],b[N];
void solve(){
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i];
    sort(b+1,b+1+n);
    ll ans=0;
    if(n==1){cout<<0;return;}
    for(ll i=1;i<=n;i++){
        ll k=max(abs(a[i]-b[1]),abs(a[i]-b[n]));
        ans=max(ans,k+i*m);
    }
    cout<<ans;
}
 
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    //int t;cin>>t;
    //while(t--)solve(),cout<<'\n';
    solve();
    return 0;
}

你可能感兴趣的:(第十五届中北大学算法与程序设计竞赛(公开赛)题解)