牛客IOI周赛17-普及组题解

题目链接

A.夹娃娃

题意:
给 定 n 个 值 a i 给定n个值a_i nai
q 次 询 问 , 每 次 给 出 l 和 r q次询问,每次给出l和r qlr
求 出 区 间 l 到 r 的 和 求出区间l到r的和 lr
题解:
n < = 1 e 5 , k < = 1 e 6 n<=1e5,k<=1e6 n<=1e5,k<=1e6
很 明 显 暴 力 是 不 行 的 很明显暴力是不行的
但 是 这 个 方 法 还 是 很 多 的 但是这个方法还是很多的
最 简 单 的 就 是 直 接 前 缀 和 最简单的就是直接前缀和
s u m [ r ] − s u m [ l − 1 ] 就 是 区 间 的 和 sum[r]-sum[l-1]就是区间的和 sum[r]sum[l1]
除 此 之 外 树 状 数 组 , 线 段 树 都 是 可 以 的 , 但 是 需 要 l o g 的 复 杂 度 除此之外树状数组,线段树都是可以的,但是需要log的复杂度 线log
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'
#define SZ(x) (int)x.size()
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[8][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};
int a[maxn],sum[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,k;cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i],sum[i]=sum[i-1]+a[i];
    while(k--){
        int l,r;
        cin>>l>>r;
        cout<<sum[r]-sum[l-1]<<endl;
    }
    return 0;
}

B.莫的难题

题意:
问 用 1 , 2 , 3 , 5 , 9 组 成 的 数 里 问用1,2,3,5,9组成的数里 12359
第 C n m m o d   1 e 9 + 7 小 的 是 哪 个 第C_n^mmod~1e9+7小的是哪个 Cnmmod 1e9+7
题解:
首 先 肯 定 要 把 这 个 组 合 数 求 出 来 首先肯定要把这个组合数求出来
正 常 的 阶 乘 逆 元 操 作 先 求 出 数 组 , 看 是 第 几 位 数 正常的阶乘逆元操作先求出数组,看是第几位数
然 后 由 于 数 的 每 一 位 是 这 五 位 数 组 成 然后由于数的每一位是这五位数组成
这 完 全 可 以 当 成 一 个 五 进 制 数 这完全可以当成一个五进制数
1 , 2 , 3 , 5 , 9 对 应 的 分 别 是 0 , 1 , 2 , 3 , 4 1,2,3,5,9对应的分别是0,1,2,3,4 1235901234
这 样 就 成 了 一 个 找 五 进 制 数 的 过 程 这样就成了一个找五进制数的过程
但 是 会 发 现 , 直 接 这 样 找 出 来 的 答 案 并 不 是 我 们 想 要 的 但是会发现,直接这样找出来的答案并不是我们想要的
因 为 这 个 标 记 第 几 小 的 数 是 一 个 十 进 制 , 他 是 从 0 开 始 的 因为这个标记第几小的数是一个十进制,他是从0开始的 0
但 是 对 于 这 个 数 组 来 说 , 第 1 小 的 数 是 1 但是对于这个数组来说,第1小的数是1 11
然 而 你 想 取 到 1 , 应 该 是 一 个 第 0 小 的 数 才 对 应 的 是 1 然而你想取到1,应该是一个第0小的数才对应的是1 101
总 的 来 说 意 思 就 是 , 对 于 正 常 的 五 进 制 , 你 想 取 到 的 第 1 小 的 数 是 0 总的来说意思就是,对于正常的五进制,你想取到的第1小的数是0 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'
#define SZ(x) (int)x.size()
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[8][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};

ll fact[maxn],inv1[maxn];
ll Pow(ll a, ll b){
	ll ans = 1;
	while(b > 0){
		if(b & 1){
			ans = ans * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
//逆元
ll inv(ll b){
    return Pow(b,mod-2)%mod;
}



ll C(ll n,ll m){
    if(m>n||n<0||m<0)return 0;
    if(m==0||m==n) return 1;
    ll res=(fact[n]*inv1[m]%mod*inv1[n-m])%mod;
    return res;
}

void init() {
	fact[0] = 1;
	for (int i = 1; i < maxn; i++) {
		fact[i] = fact[i - 1] * i %mod;
	}
	inv1[maxn - 1] = Pow(fact[maxn - 1], mod - 2);
	for (int i = maxn - 2; i >= 0; i--) {
		inv1[i] = inv1[i + 1] * (i + 1) %mod;
	}
}

int a[]={1,2,3,5,9};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int _;
    cin>>_;
    init();
    while(_--){
        ll n,m;
        cin>>n>>m;
        ll x=C(n,m);
        stack<int> s;
        while(x>0){
            x--;
            s.push(x%5);
            x/=5;
        }
        while(s.size())cout<<a[s.top()],s.pop();
        cout<<endl;
    }

    return 0;
}


C. 不平衡数组

题意:
给 2 个 长 度 为 n 的 数 组 a i , b i 给2个长度为n的数组a_i,b_i 2naibi
你 可 以 使 某 一 个 a i 加 一 , 但 是 对 应 每 个 a i 只 能 加 一 一 次 你可以使某一个a_i加一,但是对应每个a_i只能加一一次 使aiai
加 一 的 时 候 需 要 花 费 b 的 代 价 加一的时候需要花费b的代价 b
问 使 得 数 组 中 相 邻 数 都 不 相 等 , 最 少 需 要 多 少 代 价 问使得数组中相邻数都不相等,最少需要多少代价 使
题解:
n < = 1 e 5 n<=1e5 n<=1e5
这 道 题 我 最 先 想 到 的 就 是 d f s 剪 枝 这道题我最先想到的就是dfs剪枝 dfs
因 为 每 个 a i 只 能 加 一 次 因为每个a_i只能加一次 ai
所 以 只 要 考 虑 这 个 a i 到 底 加 不 加 所以只要考虑这个a_i到底加不加 ai
对 每 种 情 况 判 断 一 下 是 否 可 行 , 进 行 剪 枝 对每种情况判断一下是否可行,进行剪枝

很 明 显 , 这 个 n 的 数 据 范 围 会 导 致 超 时 很明显,这个n的数据范围会导致超时 n
但 是 其 实 换 一 种 方 法 分 析 但是其实换一种方法分析
每 个 位 置 有 两 种 状 态 , 如 果 只 考 虑 每 个 状 态 的 上 一 个 数 和 他 的 状 态 每个位置有两种状态,如果只考虑每个状态的上一个数和他的状态
然 后 进 行 转 移 , 这 不 就 是 d p 问 题 了 然后进行转移,这不就是dp问题了 dp
所 以 d p [ i ] [ 2 ] 前 i 个 数 里 第 i 个 数 是 否 加 的 状 态 的 最 小 代 价 所以dp[i][2]前i个数里第i个数是否加的状态的最小代价 dp[i][2]ii
然 后 如 果 上 个 数 加 1 不 等 于 这 个 数 然后如果上个数加1不等于这个数 1
d p [ i ] [ 0 ] = d p [ i − 1 ] [ 1 ] dp[i][0]=dp[i-1][1] dp[i][0]=dp[i1][1]
如 果 上 个 数 不 等 于 这 个 数 加 一 如果上个数不等于这个数加一
d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + b [ i ] dp[i][1]=dp[i-1][0]+b[i] dp[i][1]=dp[i1][0]+b[i]
用 这 样 可 以 对 两 个 数 的 所 有 状 态 进 行 转 移 用这样可以对两个数的所有状态进行转移
还 有 除 此 之 外 的 两 种 转 移 详 见 代 码 还有除此之外的两种转移详见代码
最 后 答 案 就 是 m i n ( d p [ n ] [ 1 ] , d p [ n ] [ 0 ] ) 最后答案就是min(dp[n][1],dp[n][0]) min(dp[n][1],dp[n][0])
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'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=911451407;
//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[8][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};
 
 
ll n,ans=1e18;
ll a[maxn],b[maxn];
ll dp[maxn][2];
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i]>>b[i];
    for(int i=1;i<=n;i++)dp[i][1]=dp[i][0]=1e18;
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        if(a[i]+1!=a[i-1])dp[i][1]=min(dp[i][1],dp[i-1][0]+b[i]);
        if(a[i]!=a[i-1]+1)dp[i][0]=min(dp[i][0],dp[i-1][1]);
        if(a[i]!=a[i-1])dp[i][0]=min(dp[i][0],dp[i-1][0]);
        if(a[i]+1!=a[i-1]+1)dp[i][1]=min(dp[i][1],dp[i-1][1]+b[i]);
    }
    cout<<min(dp[n][1],dp[n][0]);
    return 0;
}

D. 数列统计

题意:
以 x 结 尾 的 长 度 为 l 的 非 降 数 组 有 多 少 种 以x结尾的长度为l的非降数组有多少种 xl
题解:
这 道 题 可 以 进 行 一 个 递 推 这道题可以进行一个递推
因 为 长 度 为 l 的 以 x 结 尾 的 数 肯 定 能 从 两 种 数 递 推 因为长度为l的以x结尾的数肯定能从两种数递推 lx
一 个 是 长 度 为 l − 1 以 1 到 x − 1 结 尾 的 数 一个是长度为l-1以1到x-1结尾的数 l11x1
另 一 个 是 长 度 为 1 到 l − 1 以 x 结 尾 的 数 另一个是长度为1到l-1以x结尾的数 1l1x
但 是 我 们 一 直 递 推 可 以 减 去 第 二 种 但是我们一直递推可以减去第二种
我 们 把 第 一 种 改 为 长 度 为 l − 1 以 1 到 x 结 尾 的 数 我们把第一种改为长度为l-1以1到x结尾的数 l11x
这 样 递 推 下 来 就 把 第 二 种 情 况 全 部 覆 盖 这样递推下来就把第二种情况全部覆盖
所 以 就 形 成 了 一 个 递 推 式 所以就形成了一个递推式
d p [ l ] [ x ] = ∑ i = 1 x d p [ l − 1 ] [ i ] dp[l][x]=\sum_{i=1}^xdp[l-1][i] dp[l][x]=i=1xdp[l1][i]
设 置 好 边 界 值 即 可 使 用 递 推 式 即 可 求 得 结 果 设置好边界值即可使用递推式即可求得结果 使

但 是 我 一 开 始 没 有 想 到 这 个 递 推 式 但是我一开始没有想到这个递推式
我 用 的 是 打 表 的 方 法 我用的是打表的方法
在这里插入图片描述
这 一 看 , 就 很 明 显 了 , 杨 辉 三 角 这一看,就很明显了,杨辉三角
只 不 过 这 个 行 数 是 斜 着 的 , 可 以 算 出 来 只不过这个行数是斜着的,可以算出来
行 数 就 是 l + x − 1 , 列 就 是 第 l 个 行数就是l+x-1,列就是第l个 l+x1l
直 接 用 杨 辉 三 角 公 式 C n − 1 m − 1 代 入 直接用杨辉三角公式C_{n-1}^{m-1}代入 Cn1m1
结 果 就 是 C l + x − 2 l − 1 结果就是C_{l+x-2}^{l-1} Cl+x2l1
逆 元 快 速 幂 什 么 的 计 算 一 下 就 行 了 逆元快速幂什么的计算一下就行了
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'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=911451407;
//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[8][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};
 
ll fact[maxn],inv1[maxn];
ll Pow(ll a, ll b){
    ll ans = 1;
    while(b > 0){
        if(b & 1){
            ans = ans * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
//逆元
ll inv(ll b){
    return Pow(b,mod-2)%mod;
}
 
 
 
ll C(ll n,ll m){
    if(m>n||n<0||m<0)return 0;
    if(m==0||m==n) return 1;
    ll res=(fact[n]*inv1[m]%mod*inv1[n-m])%mod;
    return res;
}
 
void init() {
    fact[0] = 1;
    for (int i = 1; i < maxn; i++) {
        fact[i] = fact[i - 1] * i %mod;
    }
    inv1[maxn - 1] = Pow(fact[maxn - 1], mod - 2);
    for (int i = maxn - 2; i >= 0; i--) {
        inv1[i] = inv1[i + 1] * (i + 1) %mod;
    }
}
 
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int _;
    init();
    while(cin>>_){
        while(_--){
            ll l,x;
            cin>>l>>x;
            cout<<C(l+x-2,x-1)<<endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值