【非官方题解】2022牛客寒假算法基础集训营3

2022牛客寒假算法基础集训营3_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ

目录

A-智乃的Hello XXXX

B-智乃买瓜

C-智乃买瓜(another version)

D-智乃的01串打乱

E-智乃的数字积木(easy version)

F-智乃的数字积木(hard version)

G-智乃的树旋转(easy version)

H-智乃的树旋转(hard version)

I-智乃的密码

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

K-智乃的C语言模除方程(another version)

L-智乃的数据库


A-智乃的Hello XXXX

原题链接:https://ac.nowcoder.com/acm/contest/23478/A

解题思路:签到题,直接输出即可。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    cout << "hello world" << endl;
    return 0;
}

B-智乃买瓜

原题链接:https://ac.nowcoder.com/acm/contest/23478/B

解题思路:简单背包问题。定义dp[i][j]为考虑到第 i 个西瓜时的总重量为 j 的方案数。根据题目要求,状态转移方程为dp[i][j] = dp[i - 1][j] + dp[i - 1][j - w_i] + dp[i - 1][j - \frac{w_i}{2}],对于后两项注意判断 j 和 w[i] 的关系,初始化dp[0][0] = 1,则dp[n][1]dp[n][m]即为答案。

AC代码(未优化):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 1005, M = 1005;
int dp[N][M], w[N];

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, m;
    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] >= 0) dp[i][j] = (dp[i][j] + dp[i - 1][j - w[i]]) % mod;
            if(j - w[i] / 2 >= 0) dp[i][j] = (dp[i][j] + dp[i - 1][j - w[i] / 2]) % mod;
        }
    }
    for(int i = 1; i < m; i++) cout << dp[n][i] << ' ';
    cout << dp[n][m] << endl;
    return 0;
}

AC代码(优化):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
const int M = 1005;
int dp[M];

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        int w;
        cin >> w;
        dp[0] = 1;
        for(int j = m; j >= w / 2; j--){
            if(j >= w) dp[j] = (dp[j] + dp[j - w]) % mod;
            dp[j] = (dp[j] + dp[j - w / 2]) % mod;
        }
    }
    for(int i = 1; i <= m; i++) cout << dp[i] << ' ';
    return 0;
}

C-智乃买瓜(another version)

原题链接:https://ac.nowcoder.com/acm/contest/23478/C

解题思路:逆向思维,由于瓜的重量一定是偶数,所以总重量为 1 一定是购买重量为 2 的瓜所得到的,即如果 dp[1] 为 1,那么一定存在一个重量为 2 的瓜(重量最小瓜)。那么每次得到最小的瓜之后就需要更新当前这个瓜会造成影响的 dp 数组,这样才能将这个瓜拿出并消除它造成的影响,这时下一个 dp 数组不为 0 的点就一定是对应买第二小的瓜的一半(因为下一个 dp 数组的点对应的重量的瓜已经被拿走并消除影响了),然后再将这个瓜拿出……

考虑到上一题的转移方程dp[i][j] = dp[i - 1][j] + dp[i - 1][j - w_i] + dp[i - 1][j - \frac{w_i}{2}],当前这个瓜造成的影响有三种:第一项表示不买当前瓜,第二项表示买当前瓜,第三项表示买当前瓜的一半。消去当前瓜所造成的影响,就相当于求不买当前瓜的方案数,也就是dp[i - 1][j]。可以通过移项并让 i = i + 1,得到逆转移方程dp[i][j] = dp[i + 1][j] - dp[i][j - w_i] - dp[i][j - \frac{w_i}{2}]。最后将二维 dp 数组优化成一维即可。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 1005;
int dp[N], ans[N];

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int m, cnt = 0;
    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]){    // dp[i] != 0表示一定存在2*i重量的瓜
            ans[cnt++] = 2 * i;
            for(int j = 0; j <= m - i; j++){
                // 更新买2*i的瓜对后面dp数组的影响
                if(2 * i + j <= m) dp[2 * i + j] = (dp[2 * i + j] - dp[j] + mod) % mod;
                // 更新买2*i的瓜的一半对后面dp数组的影响
                dp[i + j] = (dp[i + j] - dp[j] + mod) % mod;
            }
        }
    }
    cout << cnt << endl;
    for(int i = 0; i < cnt; i++) cout << ans[i] << ' ';
    return 0;
}

D-智乃的01串打乱

原题链接:https://ac.nowcoder.com/acm/contest/23478/D

解题思路:签到题。遍历一遍字符串,将任意的0和1交换即可。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n;
    cin >> n;
    string s;
    cin >> s;
    for(int i = 1; i < n; i++){
        if(s[i] != s[0]){
            swap(s[i], s[0]);
            break;
        }
    }
    cout << s << endl;
    return 0;
}

E-智乃的数字积木(easy version)

原题链接:https://ac.nowcoder.com/acm/contest/23478/E

解题思路:要使得数字最大,那就是将可以交换的一段区间里的数按9~0的顺序进行排序,可以交换的条件是颜色相同,所以就遍历一遍颜色,然后遇到相邻颜色不同的就分段,同时将前面这一段的数字逆序排序。由于数字很大,将数字用字符串的形式存储,输出答案时应将字符转成数字取模再输出。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>
#include <functional>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 100005;
int col[N];
int n, m, k;
char s[N];

void op(){
    int l = 0;
    int x = col[0];
    for(int i = 1; i <= n; i++){
        if(col[i] != x){
            sort(s + l, s + i, greater<char>());
            l = i;
            x = col[i];
        }
    }
    ll ans = 0;
    for(int i = 0; i < n; i++){
        ans = (ans * 10 % mod + s[i] - '0') % mod;
    }
    cout << ans << endl;
}

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    cin >> n >> m >> k;
    cin >> s;
    for(int i = 0; i < n; i++) cin >> col[i];
    col[n] = -1;
    op();
    while(k--){
        int p, q;
        cin >> p >> q;
        for(int i = 0; i < n; i++){
            if(col[i] == p) col[i] = q;
        }
        op();
    }
    return 0;
}

F-智乃的数字积木(hard version)

原题链接:https://ac.nowcoder.com/acm/contest/23478/F

解题思路:对于每一个块(连续的颜色相同的积木),统计每个数字出现的次数。为了让一个块中的数字最大,那一定是形如 9998877654333210 这样的数字序列,将每种数字单独拎出来,可以发现如 333000 这样一个数可以表示成 \frac{(10^7 - 1)}{9} * 3,一般式为\frac{(10^n - 1)}{9} * m,那么这样就可以将每种数字的个数进行一通计算作为开始的下标 n,然后数字作为 m,就可以O(1)计算任意一个块中的答案。再考虑染色后合并的问题,可以考虑启发式合并思想,每次将较小的块往较大的块上合并,这样每次只需要遍历较小的块的元素直接加到较大的块上,由于每次合并结束后再找较小的块一定会是之前的两倍以上,因此时间复杂度O(NlogN),其中颜色的不同可以用并查集进行维护。最后,对于这种维护全局答案的类型,可以不用每次重新计算一遍答案,只需要在上次修改的基础上对于这次修改进行更改即可。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>
#include <vector>
#include <set>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 100005;
char s[N];
int fa[N], cnt[N][10];    // cnt[i][j]表示第i个块中j数字的个数
ll pw[N], ans;    // pw[i]表示10^i
int n, m, k;
vector<int> col(N);
vector<set<int> > vs(N);

int find(int x){
    if(fa[x] != x) fa[x] = find(fa[x]);
    return fa[x];
}

ll quick_pow(ll a, ll b){
    ll res = 1;
    while(b){
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

ll fun(int i){
    ll res = 0;
    for(int j = i, k = 9; k >= 0; k--){
        j += cnt[i][k];
        res = (res + k * (pw[cnt[i][k]] - 1) * quick_pow(9, mod - 2) % mod * pw[n - j + 1]) % mod;
    }
    return res;
}

void merge(int x){
    int u = find(x), v = find(x + 1);
    if(u == v) return;
    ans = ((ans - fun(u) + mod) % mod - fun(v) + mod) % mod;
    fa[v] = u;
    for(int i = 0; i <= 9; i++){
        cnt[u][i] += cnt[v][i];
    }
    ans = (ans + fun(u)) % mod;
}

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    scanf("%d%d%d", &n, &m, &k);
    scanf("%s", s + 1);
    pw[0] = 1;
    for(int i = 1; i <= n; i++){
        scanf("%d", &col[i]);
        vs[col[i]].insert(i);
        fa[i] = i;
        pw[i] = pw[i - 1] * 10 % mod;
        ans = (ans * 10 + (s[i] - '0')) % mod;
        cnt[i][s[i] - '0']++;
    }
    for(int i = 1; i < n; i++){
        if(col[i] == col[i + 1]) merge(i);
    }
    printf("%lld\n", ans);
    while(k--){
        int u, v;
        scanf("%d%d", &u, &v);
        if(u != v){
            if(vs[u].size() > vs[v].size()) swap(vs[u], vs[v]);
            for(auto x : vs[u]){
                if(vs[v].count(x - 1)) merge(x - 1);
                if(vs[v].count(x + 1)) merge(x);
                vs[v].insert(x);
            }
            vs[u].clear();
        }
        printf("%lld\n", ans);
    }
    return 0;
}

G-智乃的树旋转(easy version)

原题链接:https://ac.nowcoder.com/acm/contest/23478/G

解题思路:由于本题操作数限定为 K \leqslant 1,那么如果可以旋转回去,操作数为1;旋转不回去操作数为0。判断能否旋转回原样就是能否找到一对结点的父子关系发生交换。若找到,则以原树的父结点为轴旋转即可。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1005;
int t[N], t1[N];
int lch, rch;

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> lch >> rch;
        t[lch] = i;
        t[rch] = i;
    }
    for(int i = 1; i <= n; i++){
        cin >> lch >> rch;
        t1[lch] = i;
        t1[rch] = i;
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            if(t[i] == j && t1[j] == i){
                cout << 1 << endl << j << endl;
                return 0;
            }
        }
    }
    cout << 0 << endl;
    return 0;
}

H-智乃的树旋转(hard version)

原题链接:https://ac.nowcoder.com/acm/contest/23478/H

解题思路:从根开始先序遍历原来的二叉树中每个结点,如果该结点在打乱的树中需要还原(不在原位上),则用splay伸展树进行操作将其旋转至原位,然后打上lazy标记表示该结点已经恢复原位,不再进行旋转。每次旋转时将要旋转的结点信息存入答案数组中。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>
#include <vector>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1005;
bool vis[N], lazy[N];    // vis[i]表示i结点是否有父亲
                         // lazy[i]表示i结点是否已被还原
vector<int> ans;

struct tree{
    int fa;    // 父亲结点信息
    int ch[2];    // 0表示左孩子,1表示右孩子
}t[N], t1[N];

void rotate(int x){    // x是要旋转的结点
    int y = t1[x].fa;
    int z = t1[y].fa;
    int k = (t1[y].ch[1] == x);
    t1[z].ch[(t1[z].ch[1] == y)] = x;
    t1[x].fa = z;
    t1[y].ch[k] = t1[x].ch[k ^ 1];
    t1[t1[x].ch[k ^ 1]].fa = y;
    t1[x].ch[k ^ 1] = y;
    t1[y].fa = x;
    ans.push_back(x);
}

void splay(int x){
    while(!lazy[t1[x].fa]){
        int y = t1[x].fa;
        int z = t1[y].fa;
        if(z && !lazy[z]){
            (t1[z].ch[0] == y) ^ (t1[y].ch[0] == x) ? rotate(x) : rotate(y);
        }
        rotate(x);
    }
}

void dfs(int root){    // 先序遍历原树中的每个结点
    splay(root);
    lazy[root] = true;
    if(t[root].ch[0]) dfs(t[root].ch[0]);
    if(t[root].ch[1]) dfs(t[root].ch[1]);
}

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, root;
    cin >> n;
    for(int i = 1; i <= n; i++){
        int x, y;
        cin >> x >> y;
        vis[x] = vis[y] = true;
        t[i].ch[0] = x;
        t[i].ch[1] = y;
        if(x) t[x].fa = i;
        if(y) t[y].fa = i;
    }
    for(int i = 1; i <= n; i++){
        int x, y;
        cin >> x >> y;
        t1[i].ch[0] = x;
        t1[i].ch[1] = y;
        if(x) t1[x].fa = i;
        if(y) t1[y].fa = i;
    }
    for(int i = 1; i <= n; i++){    // 找根结点
        if(!vis[i]){
            root = i;
            break;
        }
    }
    lazy[0] = true;
    dfs(root);
    cout << ans.size() << endl;
    for(vector<int>::iterator it = ans.begin(); it != ans.end(); it++){
        cout << *it << endl;
    }
    return 0;
}

I-智乃的密码

原题链接:https://ac.nowcoder.com/acm/contest/23478/I

解题思路:将四种类型用1~4存储,dp[i][j]表示前 i 个字符中 j 类字符的数量。遍历密码长度左端点,二分答案,判断条件为是否存在三种字符,存在就往左二分,不存在就往右二分。每次二分结束后把区间长度累加起来即为答案。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 100005;
int a[N], dp[N][5];
char s[N];

bool check(int l, int r){
    int c1 = dp[r][1] - dp[l - 1][1];
    int c2 = dp[r][2] - dp[l - 1][2];
    int c3 = dp[r][3] - dp[l - 1][3];
    int c4 = dp[r][4] - dp[l - 1][4];
    if(c1 && c2 && c3 || c1 && c2 && c4 || c1 && c3 && c4 || c2 && c3 && c4) return true;
    return false;
}

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, L, R;
    cin >> n >> L >> R;
    cin >> s + 1;
    for(int i = 1; i <= n; i++){
        if(s[i] >= 'A' && s[i] <= 'Z') a[i] = 1;
        else if(s[i] >= 'a' && s[i] <= 'z') a[i] = 2;
        else if(s[i] >= '0' && s[i] <= '9') a[i] = 3;
        else a[i] = 4;
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= 4; j++){
            dp[i][j] = dp[i - 1][j] + (a[i] == j);
        }
    }
    ll cnt = 0;
    for(int i = 1; i <= n - L + 1; i++){
        int r = min(i + R - 1, n);
        int l = i;
        if(check(l, r)){
            while(l < r){
                int mid = l + r >> 1;
                if(check(i, mid)) r = mid;
                else l = mid + 1;
            }
            l = max(l, i + L - 1);
            cnt += min(i + R - 1, n) - l + 1;
        }
    }
    cout << cnt << endl;
    return 0;
}

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

原题链接:https://ac.nowcoder.com/acm/contest/23478/J

解题思路:由于[l, r]和[L, R]均可能包含正数、负数和零,首先最好将讨论的范围全部移到正半轴。

第一种情况是零存在于[l, r]区间中,那么单独计算当 Q 为 0 时,满足 x % p = 0 的解的个数,即[L, R]中 p 的整数倍的个数(这里可以学习一种比较妙的方法,设定一个足够大且为p的整数倍的数base,L和R加上后就一定能转移到正半轴,排除负数向下取整的问题)。

第二种情况是存在正半轴的数,那么直接计算正半轴的数即可。

第三种情况是存在负半轴的数,那么将其取负号转成正半轴的数再进行计算。

上面三种情况分别对应标程中的三个if语句,这样就把整个Q区间和答案区间都考虑完了。

接下来就是如何计算的问题了,可以考虑 x 存在于答案区间[L, R],Q存在于取值区间[l, r],那么要同时满足这两个条件,也就是考虑一个二元函数f(x, q) = \begin{cases} 1, & x \;%\; p = q \\ 0, & x \;%\; p \neq q \end{cases}在满足对应区间的和。这就很容易想到可以用二维前缀和来求解。

所以答案为sum(R, r) - sum(L - 1, r) - sum(R, l - 1) + sum(L - 1, l - 1)

其中x\in [L, R]q \in [l, r]。最后前缀和的求解参照下面代码。

AC代码(参照标程):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
ll p, l, r, L, R, ans;

ll sum(ll x, ll q){
    q = min(p - 1, q);
    ll round = (x + 1) / p;    // 循环节个数
    ll last = (x + 1) % p;    // 最后一个不完全循环节的元素数
    return (q + 1) * round + min(last, q + 1) - 1;
    // q + 1表示循环节中的元素数,min(last, q + 1)表示最后不完全循环节中需要的元素数,-1是因为要去掉0这个数
}

ll cal(ll l, ll r, ll L, ll R){
    return sum(R, r) - sum(L - 1, r) - sum(R, l - 1) + sum(L - 1, l - 1);
}

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    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 += cal(max((ll)1, l), r, max((ll)1, L), R);
    }
    if(L < 0 && l < 0){
        ans += cal(max((ll)1, -r), -l, max((ll)1, -R), -L);
    }
    cout << ans << endl;
    return 0;
}

K-智乃的C语言模除方程(another version)

原题链接:https://ac.nowcoder.com/acm/contest/23478/K

解题思路:由于该题的方程变为P % x = Q,P为给定的值,因此只需要考虑 P 的正负即可,当 P 为负数时,将 P 取反,并将 Q 的区间取到正半轴;当 P 为正数时,直接取 Q 的正半轴区间。接下来由于 x 是变量,P % x = P - \left \lfloor \frac{P}{x} \right \rfloor * x,可以使用整除分块进行枚举,每次枚举的时候\left \lfloor \frac{P}{x} \right \rfloor是个定值,这样Q = P - \left \lfloor \frac{P}{x} \right \rfloor * x就是一个等差数列,那么问题就转换成等差数列的第 L 项到第 R 项中有多少项是在 Q 的取值范围 [l, r] 中。

AC代码(参照标程):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
ll p, l, r, L, R, ans;

ll segment_intersect(ll lx, ll rx, ll lq, ll rq){    // 取区间交集
    ll l = max(lx, lq);
    ll r = min(rx, rq);
    return max((ll)0, r - l + 1);
}

ll cal(ll x, ll y, ll base, ll len){
    if(y == 0) return l <= x && x <= r ? segment_intersect(L, R, base, base + len) + segment_intersect(L, R, -(base + len), -base) : 0;
    ll s = x > r ? (x - r + y - 1) / y : 0;    // 项数左端点偏移量
    ll t = x >= l ? (x - l) / y : -1;    // 项数右端点偏移量
    t = min(t, len - 1);
    return segment_intersect(L, R, base + s, base + t) + segment_intersect(L, R, -(base + t), -(base + s));
}

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    cin >> p >> l >> r >> L >> R;
    if(p < 0){
        p = -p;
        l = -l;
        r = -r;
        swap(l, r);
    }
    for(ll l = 1, r; l <= p; l = r + 1){
        r = p / (p / l);
        ans += cal(p % l, p / l, l, r - l + 1);
    }
    ans += cal(p, 0, p + 1, 1000000000);
    cout << ans << endl;
    return 0;
}

L-智乃的数据库

原题链接:https://ac.nowcoder.com/acm/contest/23478/L

解题思路:按题目意思模拟即可。由于题目的数据比较多,要注意字符串的输入和各个数据的存储,下面代码参考过题大佬的代码(原因绝对不是自己的AC代码太乱了

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <utility>
#include <vector>
#include <map>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1005, M = 1005;
int a[N][M];
map<string, int> mp;
vector<int> v, v1;
map<vector<int>, int> mp2;

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= m; i++){
        string name;
        cin >> name;
        mp[name] = i;
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> a[i][j];
        }
    }
    string s, q;
    while(cin >> s){
        if(s == "BY"){
            cin >> s;
            break;
        }
    }
    int len = s.size();
    for(int i = 0; i < len; i++){
        if(s[i] == ',' || s[i] == ';'){
            v.push_back(mp[q]);
            q.clear();
        }
        else q += s[i];
    }
    for(int i = 1; i <= n; i++){
        v1.clear();
        int vlen = v.size();
        for(int j = 0; j < vlen; j++){
            v1.push_back(a[i][v[j]]);
        }
        mp2[v1]++;
    }
    cout << mp2.size() << endl;
    for(map<vector<int>, int>::iterator it = mp2.begin(); it != mp2.end(); it++){
        cout << it->second << ' ';
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值