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

题目链接

B.真的是签到题

题意:
输 出 三 遍 N U C 2020 ! ! ! 输出三遍NUC2020!!! NUC2020!!!
题解:
确 实 是 真 ! 签 到 题 , p u t s 输 出 确实是真!签到题,puts输出 puts
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
 
 
int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    puts("NUC2020!!!");
    puts("NUC2020!!!");
    puts("NUC2020!!!");
    return 0;
}

A.俄罗斯方块

题意:
10 ∗ 10 的 图 10*10的图 1010
给 四 种 俄 罗 斯 的 图 形 , 按 俄 罗 斯 的 游 戏 规 则 , 下 降 遇 到 即 停 给四种俄罗斯的图形,按俄罗斯的游戏规则,下降遇到即停
但 是 如 果 遇 到 整 行 满 的 情 况 不 消 除 但是如果遇到整行满的情况不消除
给 出 n 次 下 降 的 图 形 和 最 左 块 的 坐 标 给出n次下降的图形和最左块的坐标 n
题解:
n < = 10 并 且 图 还 很 小 n<=10并且图还很小 n<=10
直 接 模 拟 下 降 过 程 即 可 直接模拟下降过程即可
对 每 个 方 块 的 下 落 , 判 断 每 个 点 下 个 位 置 是 否 会 碰 到 东 西 对每个方块的下落,判断每个点下个位置是否会碰到东西 西
如 果 没 有 碰 到 就 继 续 下 落 , 碰 到 了 就 停 止 , 把 图 绘 上 去 即 可 如果没有碰到就继续下落,碰到了就停止,把图绘上去即可
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
 
int a[20][20];
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    cin>>n;
    for(int i=1,x,y;i<=n;i++){
        cin>>y>>x;
        if(y==1){
            int z=1;
            while(!a[z+1][x]&&!a[z+1][x+1]&&z<10)z++;
            a[z][x]=a[z-1][x]=a[z][x+1]=a[z-1][x+1]=1;
        }
        else if(y==2){
            int z=1;
            while(!a[z+1][x]&&!a[z+1][x+1]&&!a[z+1][x+2]&&z<10)z++;
            a[z][x]=a[z][x+1]=a[z][x+2]=a[z-1][x]=1;
        }
        else if(y==3){
            int z=1;
            while(!a[z+1][x]&&!a[z+1][x+1]&&!a[z+1][x+2]&&!a[z+1][x+3]&&z<10)z++;
            a[z][x]=a[z][x+1]=a[z][x+2]=a[z][x+3]=1;
        }
        else{
            int z=1;
            while(!a[z+1][x]&&!a[z+1][x+1]&&!a[z+1][x+2]&&z<10)z++;
            a[z][x]=a[z][x+1]=a[z][x+2]=a[z-1][x+1]=1;
        }
    }
    for(int i=1;i<=10;i++,cout<<endl)
        for(int j=1;j<=10;j++)
            cout<<a[i][j]<<' ';
    return 0;
}

K.签个到

题意:
给 n 个 数 , 进 行 m 次 操 作 给n个数,进行m次操作 nm
每 次 操 作 可 以 使 得 任 意 一 个 数 加 或 减 他 的 下 标 每次操作可以使得任意一个数加或减他的下标 使
问 m 次 操 作 后 这 个 数 列 的 极 差 最 大 是 多 少 问m次操作后这个数列的极差最大是多少 m
题解:
想 让 极 差 最 大 肯 定 是 只 修 改 一 个 数 , 把 他 修 改 成 最 大 或 最 小 想让极差最大肯定是只修改一个数,把他修改成最大或最小
记 录 一 下 数 列 的 最 大 值 最 小 值 记录一下数列的最大值最小值
对 于 每 一 个 数 , 找 他 和 最 小 值 或 最 大 值 的 最 大 差 对于每一个数,找他和最小值或最大值的最大差
通 过 对 该 位 的 值 加 减 下 标 可 以 扩 大 这 个 差 值 通过对该位的值加减下标可以扩大这个差值
看 修 改 哪 一 个 能 使 得 极 差 最 大 看修改哪一个能使得极差最大 使
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll a[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ll n,m;cin>>n>>m;
    ll mi=inf,mx=-inf;
    for(int i=1;i<=n;i++)cin>>a[i],mi=min(mi,a[i]),mx=max(mx,a[i]);
    if(n==1){cout<<0;return 0;}
    ll ans=0;
    for(int i=1;i<=n;i++){
        ll k=max(abs(a[i]-mi),abs(a[i]-mx));
        ans=max(ans,k+i*m);
    }
    cout<<ans;
    return 0;
}


E.简单的线性代数

题意:
给 一 个 矩 阵 A 和 整 数 k , 已 知 A k = 0 给一个矩阵A和整数k,已知A^k=0 AkAk=0
求 E + A 1 + … … + A k − 1 的 逆 求E+A^1+……+A^k-1的逆 E+A1++Ak1
题解:
矩 阵 和 整 数 其 实 是 可 以 类 比 的 矩阵和整数其实是可以类比的
所 以 完 全 可 以 类 比 成 等 比 数 列 所以完全可以类比成等比数列
然 后 用 一 下 等 比 数 列 的 求 和 公 式 然后用一下等比数列的求和公式
即 E − A k E − A 即\frac{E-A^k}{E-A} \quad EAEAk
然 后 题 目 要 求 的 是 这 个 式 子 的 逆 然后题目要求的是这个式子的逆
A ∗ B = E , 其 中 B 是 A 的 逆 A*B=E,其中B是A的逆 AB=EBA
所 以 E − A k E − A ∗ B = E 所以\frac{E-A^k}{E-A} \quad*B=E EAEAkB=E
B 就 是 最 终 结 果 , 并 且 A k = 0 B就是最终结果,并且A^k=0 BAk=0
化 简 式 子 最 后 B = E − A 化简式子最后B=E-A B=EA
就 是 对 矩 阵 全 体 取 负 , 对 角 线 加 1 即 可 就是对矩阵全体取负,对角线加1即可 线1
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
 
ll a[1010][1010];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ll n,k;cin>>n>>k;
    for(int i=1;i<=n;i++,cout<<endl)
        for(int j=1;j<=n;j++)
            {
                ll x;
                cin>>x;
                if(i==j)cout<<1ll-x<<' ';
                else cout<<-x<<' ';
            }
    return 0;
}

G. 数位操作1

题意:
给 一 个 整 数 n ( n < = 1 e 10 ) 给一个整数n(n<=1e10) n(n<=1e10)
找 到 一 个 最 小 的 数 a n s ( a n s > 9 ) 找到一个最小的数ans(ans>9) ans(ans>9)
a n s 需 要 满 足 他 的 每 一 位 上 数 的 乘 积 等 于 n ans需要满足他的每一位上数的乘积等于n ansn
题解:
首 先 a n s > 9 , 如 果 n 小 于 10 的 话 首先ans>9,如果n小于10的话 ans>9n10
那 么 可 以 直 接 确 定 结 果 是 ( 1 n ) 那么可以直接确定结果是(1n) (1n)
乘 积 能 组 成 n 的 数 , 肯 定 是 n 的 因 子 乘积能组成n的数,肯定是n的因子 nn
所 以 进 行 对 n 的 2 到 9 因 子 分 解 , 如 果 发 现 大 于 9 的 因 子 , 说 明 不 可 行 所以进行对n的2到9因子分解,如果发现大于9的因子,说明不可行 n299
但 是 会 发 现 有 的 因 子 可 以 和 自 己 或 和 别 的 因 子 相 乘 但是会发现有的因子可以和自己或和别的因子相乘
我 们 需 要 留 下 的 是 最 小 值 尽 量 小 的 几 个 小 于 10 的 因 子 我们需要留下的是最小值尽量小的几个小于10的因子 10
所 以 我 们 干 脆 直 接 倒 着 ( 从 9 到 2 ) 找 因 子 所以我们干脆直接倒着(从9到2)找因子 (92)
这 样 会 尽 量 让 多 出 来 的 不 会 被 乘 走 的 因 子 留 下 , 并 且 解 决 了 因 子 相 乘 还 是 在 10 以 内 的 问 题 这样会尽量让多出来的不会被乘走的因子留下,并且解决了因子相乘还是在10以内的问题 10
然 后 找 到 所 有 因 子 倒 序 输 出 即 可 然后找到所有因子倒序输出即可
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

int cnt[10];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ll n;
    while(cin>>n){
        vector<int> v;        
        if(n<10){cout<<1<<n<<endl;continue;}
        for(int i=9;i>1;i--)
            while(n%i==0)v.pb(i),n/=i;
        if(n>9){cout<<-1<<endl;continue;}
        reverse(all(v));
        for(auto i:v)cout<<i;
        cout<<endl;
    }
    return 0;
}


C. 港口

题意:
有 n 件 货 物 , 第 i 件 质 量 为 w i 有n件货物,第i件质量为w_i niwi
可 以 对 从 第 i 件 到 第 j 件 的 质 量 进 行 加 一 或 减 一 可以对从第i件到第j件的质量进行加一或减一 ij
问 至 少 多 少 次 能 让 全 部 货 物 质 量 相 等 问至少多少次能让全部货物质量相等
题解:
纯 原 题 纯原题
原题链接

这 个 是 运 用 了 差 分 这个是运用了差分
对 于 每 一 件 货 物 求 出 他 和 上 一 个 货 物 的 差 值 对于每一件货物求出他和上一个货物的差值
如 果 想 让 全 部 相 等 , 就 是 让 这 些 差 值 都 变 为 0 如果想让全部相等,就是让这些差值都变为0 0
对 于 每 次 区 间 加 操 作 , 区 间 内 部 的 差 值 不 会 改 变 对于每次区间加操作,区间内部的差值不会改变
会 改 变 的 就 是 两 端 和 外 部 的 会改变的就是两端和外部的
比 如 差 分 数 组 为 b , 区 间 L 到 R 加 1 比如差分数组为b,区间L到R加1 bLR1
那 么 b L + + , b R + 1 − − 那么b_L++,b_{R+1}-- bL++,bR+1
如 果 是 进 行 减 操 作 同 理 , b L − − , b R + 1 + + 如果是进行减操作同理,b_L--,b_{R+1}++ bL,bR+1++
所 以 问 题 转 化 , 对 差 分 数 组 进 行 多 少 次 如 下 操 作 让 数 列 全 部 变 成 0 所以问题转化,对差分数组进行多少次如下操作让数列全部变成0 0
对 于 2 < = i , j < = n , b i + + , b j − − 对于2<=i,j<=n,b_i++,b_j-- 2<=i,j<=n,bi++,bj
所 以 我 们 只 需 要 统 计 数 组 中 有 多 少 正 数 和 负 数 所以我们只需要统计数组中有多少正数和负数
然 后 同 时 消 除 两 种 数 , 直 到 一 种 为 0 然后同时消除两种数,直到一种为0 0
剩 余 多 出 来 的 数 全 部 和 1 进 行 操 作 , 使 得 全 部 为 0 即 可 剩余多出来的数全部和1进行操作,使得全部为0即可 1使0
另 x 为 正 数 和 , y 为 负 数 和 另x为正数和,y为负数和 xy
列 出 式 子 就 是 m i n ( x , y ) + a b s ( x − y ) = m a x ( x , y ) 列出式子就是min(x,y)+abs(x-y)=max(x,y) min(xy)+abs(xy)=max(xy)
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll b[maxn],a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    ll x=0,y=0;
    for(int i=1;i<=n;i++)b[i]=a[i]-a[i-1];
    for(int i=2;i<=n;i++){
        if(b[i]<0)y+=-b[i];
        else x+=b[i];
    }
    cout<<max(x,y);
    return 0;
}


F. 集合操作

题意:
有 n 次 操 作 , 操 作 分 为 3 种 有n次操作,操作分为3种 n3
1. 如 果 集 合 中 无 x , 插 入 x 1.如果集合中无x,插入x 1.xx
2. 如 果 集 合 有 有 x , 删 除 x 2.如果集合有有x,删除x 2.xx
找 到 > = x 的 最 小 的 集 合 中 没 有 的 数 找到>=x的最小的集合中没有的数 >=x
题解:
n < = 1 e 6 , x < = 1 e 9 n<=1e6,x<=1e9 n<=1e6,x<=1e9
由 于 是 要 找 一 个 没 有 的 数 由于是要找一个没有的数
如 果 暴 力 找 , n 过 大 , 如 果 已 经 插 入 很 多 连 续 数 , 会 t l e 如果暴力找,n过大,如果已经插入很多连续数,会tle ntle
但 是 如 果 想 用 一 个 s e t 维 护 不 存 在 集 合 里 的 数 但是如果想用一个set维护不存在集合里的数 set
x 又 是 1 e 9 , 会 m l e x又是1e9,会mle x1e9mle

然 后 我 就 想 到 了 可 以 用 二 分 找 一 下 不 存 在 的 数 然后我就想到了可以用二分找一下不存在的数
但 不 存 在 的 数 又 不 是 单 调 的 , 不 能 用 二 分 找 但不存在的数又不是单调的,不能用二分找
所 以 就 需 要 维 护 个 数 , 看 x 到 m i d 的 集 合 存 在 个 数 是 否 等 于 m i d − x + 1 所以就需要维护个数,看x到mid的集合存在个数是否等于mid-x+1 xmidmidx+1
但 是 x 很 大 , 维 护 这 个 个 数 仍 然 会 m l e 但是x很大,维护这个个数仍然会mle xmle
所 以 我 们 就 需 要 对 这 些 x 进 行 离 散 所以我们就需要对这些x进行离散 x
将 每 个 x 和 他 的 下 一 个 点 放 入 数 组 , 由 于 需 要 表 示 中 间 的 间 隔 将每个x和他的下一个点放入数组,由于需要表示中间的间隔 x
然 后 进 行 离 散 , 离 散 完 中 间 的 个 数 就 可 以 用 树 状 数 组 维 护 然后进行离散,离散完中间的个数就可以用树状数组维护
然 后 按 刚 才 的 思 路 进 行 二 分 查 找 即 可 然后按刚才的思路进行二分查找即可
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=2e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
 
struct node{
    int id,x;
}p[maxn];
int a[maxn],c[maxn];
int lowbit(int x){
    return x&(-x);
}
void add(int x,int v){
    while(x<maxn){
        c[x]+=v;
        x+=lowbit(x);
    }
}
int query(int x){
    int res=0;
    while(x){
        res+=c[x];
        x-=lowbit(x);
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;cin>>n;
    for(int i=1;i<=n;i++)
        cin>>p[i].id>>p[i].x,a[2*i-1]=p[i].x,a[2*i]=p[i].x+1;
    int len=2*n;
    sort(a+1,a+1+len);
    len=unique(a+1,a+1+len)-a-1;
    for(int i=1;i<=n;i++){
        int op=p[i].id;
        int x=lower_bound(a+1,a+1+len,p[i].x)-a;
        if(op==1){
            if(!(query(x)-query(x-1)))add(x,1);
        }
        if(op==2){
            if(query(x)-query(x-1))add(x,-1);
        }
        if(op==3){
            int l=x,r=len,ans=-1;
            while(l<=r){
                int mid=l+r>>1;
                if(query(mid)-query(x-1)>=mid-x+1)ans=mid,l=mid+1;
                else r=mid-1;
            }
            if(ans!=-1)cout<<a[ans]+1<<endl;
            else cout<<a[x]<<endl;
        }
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值