ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022牛客寒假集训营3

2022-01-30 19:34:13  阅读:269  来源: 互联网

标签:frac int MAX ll 牛客 2022 集训营 sum dp


题目链接:link

A.智乃的Hello XXXX

签到题1

D.智乃的01串打乱

签到题2

B.智乃买瓜(easy)

题目

水果摊上贩卖着 \(N\) 个不同的西瓜,第 \(i\) 个西瓜的重量为 \(w_i\)

智乃对于每个瓜都可以选择买一个整瓜或者把瓜劈开买半个瓜,半个瓜的重量为 \(\frac{w_i}{2}\)

智乃想要知道,如果他想要购买西瓜的重量和分别为 \(k=1,2,\cdots,M\) 时,有多少种购买西瓜的方案

分析

背包问题的简单变形:

\[dp[i][j]=dp[i-1][j]+dp[i-1][j-w_i]+dp[i-1][j-\frac{w_i}{2}] \]

代码
#include<bits/stdc++.h>
using namespace std;

const int MOD = 1e9 + 7;
const int MAX_N = 1000 + 5;
int n, m;
int w[MAX_N];
int dp[MAX_N][MAX_N];

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> w[i];
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j <= m; j++) {
            dp[i][j] = dp[i - 1][j];
            if(j >= w[i] / 2)
                dp[i][j] = (dp[i][j] + dp[i - 1][j - w[i] / 2]) % MOD;
            if(j >= w[i])
                dp[i][j] = (dp[i][j] + dp[i - 1][j - w[i]]) % MOD;
        }
    }
    for(int i = 1; i <= m; i++)
        cout << dp[n][i] << " ";
    cout << endl;
    return 0;
}

L.智乃的数据库

题目

给定一张由 \(N\) 条记录和 \(M\) 个 int 类型的字段组成的数据表,请你执行一个 SELECT COUNT(*) FROM Table GROUP BY ...; 的查询语句,请你把查询的结果告诉智乃

分析

模拟题

代码
#include<bits/stdc++.h>
using namespace std;
 
const int MAX_N = 1000 + 5;
int n, m;
string s[MAX_N];
string cmd;
int a[MAX_N][MAX_N];
vector<int> idx;
map<string, int> mt;
bool vis[MAX_N];
int ans[MAX_N];
 
bool cmp(int a1, int a2)
{
    for(int i = 0; i < idx.size(); i++)
        if(a[a1][idx[i]] != a[a2][idx[i]])
            return false;
    return true;
}
 
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= m; i++) {
        cin >> s[i];
        mt.insert(make_pair(s[i], i));
    }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> a[i][j];
    getchar();
    getline(cin, cmd);
    int l = 36;
    for(int i = 36; i < cmd.size(); i++) {
        if(cmd[i] == ',') {
            string tmp = cmd.substr(l, i - l);
            idx.push_back(mt[tmp]);
            l = i + 1;
        }
    }
    string tmp = cmd.substr(l, cmd.size() - l - 1);
    idx.push_back(mt[tmp]);
    int cnt = 0;
    for(int i = 1; i <= n; i++) {
        if(!vis[i]) {
            cnt++;
            ans[cnt]++;
            vis[i] = true;
            for(int j = i + 1; j <= n; j++) {
                if(cmp(i, j)) {
                    vis[j] = true;
                    ans[cnt]++;
                }
            }
        }
    }
    cout << cnt << endl;
    for(int i = 1; i <= cnt; i++)
        cout << ans[i] << " ";
    cout << endl;
    return 0;
}

E.智乃的数字积木(easy)

题目

智乃酱有 \(N\) 块积木,每一块积木都有自己的颜色以及数字,这 \(N\) 块积木颜色的范围从 \(1\) 到 \(M\) ,数字的范围从 \(0\) 到 \(9\)
现在智乃酱把这些积木从左到右排成一排,这样积木看上去就形成了一个大整数,智乃觉得这个数字不够大,所以他决定交换一些积木使得这个大整数尽可能的大

具体来讲,智乃可以在任意时刻无限次的交换相邻且同色的数字积木
但是即使这样,智乃觉得这个数字还是不够大

所以智乃酱拿出了她的油漆桶,她决定进行 \(K\) 次操作,每次操作都选中两种颜色 \(P,Q\) ,然后将所有颜色为 \(P\) 的积木染成颜色 \(Q\)
当然,在染色结束后智乃酱也是可以交换相邻同色积木进行调整

现在智乃想要知道,她进行 \(K\) 次染色操作之前,以及每次染色后能够通过交换得到最大的正整数是多少

分析

由于相邻同色的积木可以任意交换,当一个连续的同颜色色块的数字从大到小排序时,能取到这一色块产生的最大值,那么对于序列中的所有色块都进行一次排序,就能使序列取最大值

由于 \(K\leq 10\) ,所以在每次染色后都执行一次上述的操作即可,复杂度 \(O((K+1)\times N\log{N})\)

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
 
const int MAX_N = 100000 + 5;
const int MOD = 1e9 + 7;
int n, m, k;
string s;
int col[MAX_N];
 
int calc()
{
    ll ans = 0, pw = 1;
    for(int i = n - 1; i >= 0; i--) {
        ans = (ans + pw * (s[i] - '0')) % MOD;
        pw = pw * 10 % MOD;
    }
    return ans;
}
 
void work()
{
    int l = 0, r = 0;
    for(int i = 0; i < n - 1; i++) {
        if(col[i] == col[i + 1]) {
            r = i + 1;
            if(i + 1 == n - 1) {
                sort(s.begin() + l, s.begin() + r + 1, greater<char>());
            }
        } else {
            r = i;
            sort(s.begin() + l, s.begin() + r + 1, greater<char>());
            r = l = i + 1;
        }
    }
}
 
int main()
{
    cin >> n >> m >> k >> s;
    for(int i = 0; i < n; i++)
        cin >> col[i];
    work();
    cout << calc() << endl;
     
    while(k--) {
        int p, q;
        cin >> p >> q;
        for(int i = 0; i < n; i++)
            if(col[i] == p)
                col[i] = q;
        work();
        cout << calc() << endl;
    }
    return 0;
}

I.智乃的密码

题目

某网站的密码必须符合以下几个条件

  • 仅包含大小写英文字母,数字,和特殊符号
  • 长度大于等于 \(L\) 且小于等于 \(R\)
  • 密码中至少应该包括 ①大写英文字母,②小写英文字母,③数字,④特殊符号 这四类字符中的三种

现在给定一个长度为 \(N\) 的字符串 \(S\) ,求 \(S\) 中有多少个子串是一个符合条件的密码

分析

对于一个索引为 \(i\) 的字符,我们要求使子串 \((i,j)\) 满足作为密码的条件的最小的 \(j\) ,那么子串 \((i,j+1),(i,j+2),\cdots\) 也满足条件,就能得到以 \(i\) 为子串的第一个字符所组成的满足条件的子串数

考虑如何求 \(j\) ,首先我们将原字符串的每一个字符都替换成他们所代表的字符类型,设 \(b[i][k]\) 表示从索引 \(i\) 开始到字符串结尾,类型为 \(k\) 的字符出现的最早索引,稍加思考就可以得出:将 \(b[i][1],b[i][2],b[i][3],b[i][4]\) 从小到大排序后第三大的 \(b[i][k]\) 就是要求的 \(j\)

数组 \(b\) 的预处理可以在 \(O(n)\) 的时间里完成,总复杂度也为 \(O(n)\)

代码
#include<bits/stdc++.h>
using namespace std;
 
const int MAX_N = 100000 + 5;
const int INF = 0x3f3f3f3f;
int n, l, r;
int a[MAX_N];
int b[MAX_N][5];
string s;
 
 
int pd(char ch)
{
    if(ch >= 'A' && ch <= 'Z')
        return 1;
    else if(ch >= 'a' && ch <= 'z')
        return 2;
    else if(ch >= '0' && ch <= '9')
        return 3;
    return 4;
}
 
int mfind(int st, int val)
{
    for(int i = st; i <= n; i++)
        if(a[i] == val)
            return i;
    return -1;
}
 
int getminn(int idx)
{
    int tmp[5];
    for(int i = 1; i <= 4; i++)
        tmp[i] = b[idx][i];
    sort(tmp + 1, tmp + 4 + 1);
    return tmp[3];
}
 
int main()
{
    cin >> n >> l >> r >> s;
    for(int i = 1; i <= n; i++)
        a[i] = pd(s[i - 1]);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= 4; j++) {
            if(i > b[i - 1][j]) {
                b[i][j] = mfind(b[i - 1][j] + 1, j);
                b[i][j] = (b[i][j] == -1 ? INF : b[i][j]);
            } else {
                b[i][j] = b[i - 1][j];
            }
        }
    }
    long long ans = 0;
    for(int i = 1; i <= n; i++) {
        int res = max(i + l - 1, getminn(i));
        if(res <= i + r - 1 && res <= n) {
            ans = ans + min(n, i + r - 1) - res + 1;
        }
    }
    cout << ans << endl;
    return 0;
}

G.智乃的树旋转

题目

输入两棵二叉树,其中第二棵树由第一棵树经过不超过一次的树旋转过后形成,问如何操作使其还原回第一棵树

分析

每次旋转后,父子结点的关系都会被改变,而旋转次数又不超过一次,所以可以双重循环枚举结点 \(i,j\) 如果在第一颗树中结点 \(i\) 的父节点是 \(j\) 且在第二棵树中结点 \(j\) 的父节点是 \(i\) ,那说明他们在旋转后互换了,再旋转一次即可还原

代码
#include<bits/stdc++.h>
using namespace std;

const int MAX_N = 1000 + 5;
struct tree {
    int fa;
    int ch[2];
} t1[MAX_N], t2[MAX_N];
int n;

void build(tree * t)
{
    int x, y;
    for(int i = 1; i <= n; i++) {
        scanf("%d%d", &x, &y);
        t[i].ch[0] = x;
        t[i].ch[1] = y;
        if(x)
            t[x].fa = i;
        if(y)
            t[y].fa = i;
    }
}

int main()
{
    scanf("%d", &n);
    build(t1);
    build(t2);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(t1[j].fa == i && t2[i].fa == j) {
                printf("1\n%d\n", i);
                return 0;
            }
        }
    }   
    printf("0\n");
    return 0;
}

C.智乃买瓜(hard)

题目

在原题的购买规则上,智乃现在知道购买重量和为 \(1,2,\cdots ,M\) 的方案数,构造一个贩卖 \(N\) 个不同西瓜的水果摊,使方案数符合输入

分析

根据原来的转移方程:

\[dp[i][j]=dp[i-1][j]+dp[i-1][j-w_i]+dp[i-1][j-\frac{w_i}{2}] \]

可以推出:

\[dp[i][j]=dp[i+1][j]-dp[i][j-w_i]-dp[i][j-\frac{w_i}{2}] \]

相当于一开始给了 \(dp[n][1],dp[n][2],\cdots,dp[n][m]\) 从后向前推

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MOD = 1e9 + 7;
const int MAX_M = 1000 + 5;
ll dp[MAX_M];
int m;
vector<int> ans;

int main()
{
    cin >> m;
    dp[0] = 1;
    for(int i = 1; i <= m; i++)
        cin >> dp[i];
    for(int i = 1; i <= m; i++) {
        while(dp[i]) {
            ans.push_back(2 * i);
            for(int j = 0; j <= m; j++) {
                if(j + i <= m)
                    dp[j + i] = (dp[j + i] - dp[j] + MOD) % MOD;
                if(j + 2 * i <= m)
                    dp[j + 2 * i] = (dp[j + 2 * i] - dp[j] + MOD) % MOD;
            }
        }
    }
    cout << ans.size() << endl;
    for(int i = 0; i < ans.size(); i++)
        cout << ans[i] << " ";
    cout << endl;
    return 0;
}

J.智乃的C语言模除方程

题目

给定 \(P,l,r,L,R\) 求:

\[\sum_{i=L}^R \sum_{j=l}^r [i\bmod P=j] \]

分析

考虑原问题的简单版本,求:

\[sum(x,y)=\sum_{i=1}^x \sum_{j=1}^y [i\bmod P=j] \]

容易知道: \(sum(P-1,y)=\min(P-1,y)\) ,记 \(t=\min(P-1,y)\)

由模的循环性可知原式进行了 \(\lfloor\frac{x+1}{P}\rfloor\) 次完整循环,最后一次不完整的循环进行了 \(x-P\lfloor\frac{x+1}{P}\rfloor=(x+1)\bmod P-1\) 次,可以得到:

\[sum(x,y)=t\lfloor\frac{x+1}{P}\rfloor+\min(t,(x+1)\bmod P-1) \]

当 \(l,r,L,R\) 均为正数时,可以用二维前缀和的方式解决这一问题

当 \(l\leq0\leq r\) 时,可以发现在上式中并没有计算 \(i\bmod P=0\) 产生的贡献,在这里我们需要加上

\[L\leq kP\leq R\Rightarrow \lceil\frac{L}{P}\rceil\leq k\leq \lfloor\frac{R}{P}\rfloor \]

\(k\) 的个数为 \(\lfloor\frac{R}{P}\rfloor-\lceil\frac{L}{P}\rceil+1=\lfloor\frac{R}{P}\rfloor-\lfloor\frac{L-1}{P}\rfloor\)

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll P, l, r, L, R, ans = 0;

ll sum(ll x, ll y)
{
    ll t = min(P - 1, y);
    return (x + 1) / P * t + min((x + 1) % P - 1, t);
}

ll calc(ll a, ll b, ll c, ll d)
{
    return sum(a, b) - sum(c - 1, b) - sum(a, d - 1) + sum(c - 1, d - 1);
}

int main()
{
    cin >> P >> l >> r >> L >> R;
    P = abs(P);
    if(l <= 0 && r >= 0) {
        ll base = ((ll)1e10) / P * P;
        ans += (base + R) / P - (base + L - 1) / P;
    }
    if(R > 0 && r > 0)
        ans += calc(R, r, max(1ll, L), max(1ll, l));
    if(L < 0 && l < 0)
        ans += calc(-L, -l, max(1ll, -R), max(1ll, -r));
    cout << ans << endl;
    return 0;
}

标签:frac,int,MAX,ll,牛客,2022,集训营,sum,dp
来源: https://www.cnblogs.com/tttkf/p/15857414.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有