【题解】牛客OI周赛15-普及组

A 咪咪游戏

直接判断奇数位是否均为m,偶数位均为q即可。

/*
* @Author: wxyww
* @Date: 2020-04-03 19:18:25
* @Last Modified time: 2020-04-03 19:20:35
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<bitset>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 100010;
ll read() {
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
char s[N];
void solve() {
    scanf("%s",s + 1);
    int n = strlen(s + 1);
    if(n & 1) {
        puts("No");return;
    }
    for(int i = 1;i <= n;i += 2) 
        if(s[i] != 'm') {
            puts("No");return;
        }
    for(int i = 2;i <= n;i += 2)
        if(s[i] != 'q') {
            puts("No"); return;
        }
    puts("Yes");
}
int main() {
    int Q = read();
    while(Q--) {
        solve();
    }

    return 0;
}

B 三角形

用f[i][j]表示前i个箱子,宝物价值和为j的方案数。状态是的。转移的时候枚举当前箱子选的宝物,背包转移即可。复杂度为
总复杂度就是

最后枚举宝物价值和,找到前K中方案即可。

/*
* @Author: wxyww
* @Date: 2020-04-03 19:29:25
* @Last Modified time: 2020-04-03 20:43:42
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<bitset>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 110;
ll read() {
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
ll ans;
int f[N][N * N];
int a[N][N],len[N];
int main() {
    int n = read(),K = read();
    for(int i = 1;i <= n;++i) {
        len[i] = read();
        for(int j = 1;j <= len[i];++j)
            a[i][j] = read();
    }

    f[0][0] = 1;
    for(int i = 1;i <= n;++i) {
        for(int j = 1;j <= len[i];++j) {
            for(int k = a[i][j];k <= 10000;++k) {
                f[i][k] += f[i - 1][k - a[i][j]];
                f[i][k] = min(f[i][k],K);
            }
        }
    }

    for(int i = 1;i <= 10000;++i) {
        int w = min(K,f[n][i]);
        K -= w;
        ans += w * i;
    }
    cout<<ans;

    return 0;
}

C 区间加

一开始理解错题意了。题目要求“对于任意两次区间加的起始,结束位置各不相同。”而不是“两个区间的不能相同。”

我们用now记录下现在还没有匹配的区间左端点的个数,用表示第个点到还需要加多少。

这样如果小1,我们就要在i - 1这个位置放一个右端点,对应的左端点有now种选择,所以答案乘以now。

如果大1,我们就要在i这个位置放一个左端点,就让now++

如果a[i]与a[i-1]相等的话,我们可以在i-1处放一个右端点,再在i处放一个左端点,防止右端点的时候有now种方案,也可以什么都不做,所以就有(now+1)种方案,所以答案乘以(now+1)即可。

/*
* @Author: wxyww
* @Date: 2020-04-03 21:19:10
* @Last Modified time: 2020-04-04 07:58:55
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<bitset>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 2010,mod = 998244355;
ll read() {
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
int a[N];
int main() {
    int n = read(),m = read();
    for(int i = 1;i <= n;++i) a[i] = m - read();
    for(int i = n + 1;i >= 1;--i) a[i] -= a[i - 1];
    ll now = 0,ans = 1;
    for(int i = 1;i <= n + 1;++i) {
        if(abs(a[i]) > 1) {
            puts("0");return 0;
        }
        if(a[i] == -1) ans = ans * now-- % mod;
        if(a[i] == 0) ans = ans * (now + 1) % mod;
        if(a[i] == 1) ++now;
    }
    cout<<ans;
    return 0;
}

D 多元组

表示前个数字,选择了j个数字的方案数量。

如果暴力转移的话就是

先将一开始的数组离散化一下,然后开K个树状数组优化一下,转移的复杂度就变成了,总的复杂度就是

/*
* @Author: wxyww
* @Date: 2020-04-03 21:07:29
* @Last Modified time: 2020-04-03 21:15:18
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<bitset>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 100010;
const int mod = 1e9 + 7;
ll read() {
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
int n,m,a[N],ls[N],f[N][52];
void update(int *tree,int pos,int c) {
    while(pos <= n) {
        tree[pos] += c;
        tree[pos] >= mod ? tree[pos] -= mod : 0;
        pos += pos & -pos;
    }
}
int query(int *tree,int pos) {
    int ret = 0;
    while(pos) {
        ret += tree[pos];
        ret >= mod ? ret -= mod : 0;
        pos -= pos & -pos;
    }
    return ret;
}
int tree[52][N];
int main() {
    n = read(),m = read();
    for(int i = 1;i <= n;++i) a[i] = ls[i] = read();
    sort(ls + 1,ls + n + 1);

    for(int i = 1;i <= n;++i) a[i] = lower_bound(ls + 1,ls + n + 1,a[i]) - ls;

    ll ans = 0;
    for(int i = 1;i <= n;++i) {
        f[i][1] = 1;
        for(int j = 2;j <= m;++j)
            f[i][j] = query(tree[j - 1],a[i] - 1);
        for(int j = 1;j <= m;++j)
            update(tree[j],a[i],f[i][j]);

        ans += f[i][m];
        ans >= mod ? ans -= mod : 0;
    }
    cout<<ans;

    return 0;
}
全部评论
后面就不能再选择以1或3为端点的去见了
1 回复 分享
发布于 2020-04-04 18:13
第三题妙啊,感觉比官方题解好懂。
点赞 回复 分享
发布于 2020-04-04 22:28
确实第三题写的妙。感谢这位博主。
点赞 回复 分享
发布于 2020-04-05 01:06
但是 博主 我有一点想不太明白 就是 第三题的 a[i]为什么差值大于1就无法实现等于m 呢?
点赞 回复 分享
发布于 2020-04-05 01:16

相关推荐

3 收藏 评论
分享
牛客网
牛客企业服务