2021牛客多校三补题

2021牛客多校三补题(待更

链接:2021牛客暑期多校训练营3_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

F题

题意:

给n个(1 <= n <= 4)个1到13的数组,可以添加、加减乘除以及括号,使其最后的值等于m。问n个数的所有解都出现了分数的解的这种组合有几种,并且输出。

题解:

模拟;

当n = 1, 2,一定不可能运算过程中有分数,因为最后的值m一定是整数;

当n=3时,只有可能 (整数/整数)*整数 = 整数 ,这种情况,可以调换位置,就可以有没有整数的解

eg. 8 / 3 * 9 = 24 ==> 9 / 3 * 8 = 24,所以不满足

所以n一定等于4(这算一个优化?

总体思路时枚举数字排列(非递减的排列)

再将四个数字+三个运算符 排列组合 成为后缀表达式 来运算

当这种排列只有出现分数的解,就算进答案

几个坑点

1.我推测出现分数解的情况只有可能是所有运算符号最多出现一次,就在dfs的时候加了这个条件

实际上可能一个没有出现分数的解是多次用到一个运算符的eg 1*(3 * 5 + 9) ==(5 / 3 + 1)* 9

2.后缀运算符用stack时间太久了

用数组模拟栈自测时间:

栈自测时间:

这也差太多了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<string>ans;
int pai[5];
ll tmp[8];
bool vis1[5];
bool vis2[5];
int fcnt, icnt;
int m;
const double eps = 1e-6;
void dfs(int deep, int cnt) {
    if(icnt != 0) return;
    if(deep == 7) {
        stack<double>sta;
        bool flag = 0;
        for(int i = 0; i < 7; i++) {
            if(tmp[i] < 14) {
                sta.push(tmp[i] * 1.0);
            }
            else {
                if(sta.size() < 2) return;
                double b = sta.top(); sta.pop();
                double a = sta.top(); sta.pop();
                if(tmp[i] == 14) sta.push(a + b);
                else if(tmp[i] == 15) sta.push(a - b);
                else if(tmp[i] == 16) sta.push(a * b);
                else if(tmp[i] == 17 && abs(a) < eps) return;
                else if(tmp[i] == 17 && abs(round(b / a) - b / a) > eps) {
                    flag = 1;
                    sta.push(b / a);
                }
                else {
                    sta.push(b / a);
                }
            }
        }
        if(sta.size() != 1) return;
        else {
            if(abs(sta.top() - m * 1.0) < eps) {
                if(flag) fcnt++;
                else icnt++;
            }
            return;
        }
    }
    if(cnt < 4) {
        for(int i = 0; i < 4; i++) {
            if(vis1[i] == 0) {
                vis1[i] = 1;
                tmp[deep] = pai[i];
                dfs(deep + 1, cnt + 1);
                vis1[i] = 0;
            }
        }
    }
    if(deep - cnt < 3) {
        for(int i = 0; i < 4; i++) {
            //一定不能用vis不然可能一组答案其实有没有分数解例如1*(3 * 5 + 9)
            tmp[deep] = i + 14;
            dfs(deep + 1, cnt);
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    int n;
    cin >> n >> m;
    if(n != 4) {
        cout << 0 << endl;
        return 0;
    }
    for(int a = 1; a <= 13; a++) {
        for(int b = a; b <= 13; b++) {
            for(int c = b; c <= 13; c++) {
                for(int d = c; d <= 13; d++) {
                    //还有三个运算符没有加进去!
                    //排列--> dfs
                    pai[0] = a, pai[1] = b, pai[2] = c, pai[3] = d;
                    memset(vis1, 0, sizeof vis1);
                    fcnt = 0, icnt = 0;
                    dfs(0, 0);
                    if(fcnt && !icnt) {
                        string str = to_string(a) + " " + to_string(b) + " " + to_string(c) + " " + to_string(d);
                        ans.push_back(str);
                    }
                }
            }
        }
    }
    cout << ans.size() << endl;
    for(auto x : ans) {
        cout << x << endl;
    }
}

E题

题意:

问有多少对不同的(x,y)满足(x^2 + y^2) 整除 (xy + 1)

题解:

dmy的证明:

根据下面的网站找公式,注意一下判溢出

1 8 27 30 64 112 - OEIS
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAX = 1e6 + 10;
const ll INF = 1e18 + 100;
vector<ll> ans;
void init() {
    ans.push_back(1);
    for(ll k = 2; k < MAX; k++) {
        ll tmp1 = k;
        ll tmp2 = k * k * k;
        while(tmp2 < INF) {
            ans.push_back(tmp2);
            ll tmp = tmp2;
            if(INF / k / k < tmp2) break;//判溢出
            tmp2 = k * k * tmp2 - tmp1;
            tmp1 = tmp;
        }
    }
}

int main() {
    init();
    sort(ans.begin(), ans.end());
    //ans.erase(unique(ans.begin(), ans.end()), ans.end());
    int T;
    scanf("%d", &T); 
    while(T--) {
        ll x;
        scanf("%lld", &x);
        ll res = upper_bound(ans.begin(), ans.end(), x) - ans.begin();
        printf("%lld\n", res);
    }
}

// 1 8 27 30 64 112 125 216 240 343 418 512 729 1000 1020 1331 1560
// 1728 2133 2197 2744 3120 3375 4096 4913 5822 5832 6859 7770 8000 9261

B题

题意

n*m的矩阵,每个点上的值可以由给的公式算出

当一个矩阵的四个顶点有三个顶点时,第四个顶点可以被自动补全染色,

问把所有点都染色的最小权值和为多少

题解

经过一些操作转化为最小生成树问题

用并查集来做kruskal;

坑点

1.有n+m个点,数组和初始化的for循环要开两倍

2.sort会T,要用桶排序优化,因为sort的n是1e7

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAX = 5e3 + 10;
const ll INF = 1e9 + 7;
const int maxmax = 1e5 + 10;
ll n, m, a, b, c, d, p;
int fa[MAX * 2];
int sizee[MAX * 2];
struct node {
    int a, b, w;
} aa[MAX*MAX];

vector<pair<int, int> > ans[maxmax];
void init() {
    ll tmp = a;
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            tmp = ((tmp * tmp % p) * b % p + tmp * c % p + d) % p;
            aa[cnt].a = i;
            aa[cnt].b = j + n;
            aa[cnt].w = tmp;
            cnt++;
        }
    }
    for (int i = 0; i < MAX * 2; i++) {
        fa[i] = i;
        sizee[i] = 1;
    }
}

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

void merge(int a, int b) {
    a = find(a);
    b = find(b);
    if (a == b) return;
    if (sizee[a] > sizee[b]) swap(a, b);
    sizee[a] += sizee[b];
    fa[a] = b;
}

ll kruskal(int n, int m) {//n条边,m条点
    ll cnt = 0, res = 0;
    for (int i = 0; i < n && cnt != m - 1; i++) {
        if (find(aa[i].a) != find(aa[i].b)) {
            merge(aa[i].a, aa[i].b);
            res += aa[i].w;
            cnt++;
        }
    }
    if (cnt < m - 1) {
        //森林
    }
    return res;
}

int main() {
    scanf("%lld %lld %lld %lld %lld %lld %lld", &n, &m, &a, &b, &c, &d, &p);
    init();
    int maxx = -1;
    for(int i = 0; i < n * m; i++) {
        ans[aa[i].w].push_back(make_pair(aa[i].a, aa[i].b));
        maxx = max(maxx, aa[i].w);
    }
    int cnt = 0;
    for(int i = 0; i < maxx; i++) {
        for(auto x : ans[i]) {
            aa[cnt].a = x.first;
            aa[cnt].b = x.second;
            aa[cnt++].w = i;
        }
    }
//     sort(aa, aa + n * m, [](node a, node b) {
//         if (a.w != b.w) return a.w < b.w;
//     });
    ll ans = kruskal(n * m, n + m);
    printf("%lld", ans);
}

小结

补题未完成ing

在思维上确实有些题目没有想到怎么解,比如B题

然后数据给的也非常巧妙(就是会卡T

补题都卡了好久,赛场上就更加可能就做不出来了

但是并不是完全不可做的,心态要摆正,

F题我总觉得会有一些我不懂的性质,就不敢做,哎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值