A 简单数学

  • 题意:给一个正整数,问n是否存在,使得能整除,并且是奇数?
  • 签到题,多琢磨一下就能知道做法:判定是否是2的幂次,不是则有解
  • 这里是正经的推导:
    • 为正整数,为质数,则由唯一分解定理:
    • 我们知道,除2之外,所有的质数都是奇数。也就是说,要想在中找到奇数因子,只需要有除2之外的质数即可
    • 所以直接判定是否为2的幂次即可
    • 判定2的幂次可使用判定,若结果为0,则为2的幂次
#include <bits/stdc++.h>
using namespace std;
signed main() {
    long long n;
    while(cin >> n) {
        puts(n & (n-1) ? "YES" : "NO");
    }
    return 0;
}

B 硬币游戏

  • 题意:进行次比赛,每次比赛选取2人,硬币多的获胜,相同则随机。获胜者获得败者所有硬币,求最后可能的冠军编号
  • 首先,这个人当中硬币数最多的必可以为冠军,这是显而易见的。然后我们考虑次大的,如果它和比它硬币数少的人的硬币总和,比最大的大,那么这个人也可以为冠军,同理类推
  • 所以我们需要对整个序列排序并记下该对象的编号,然后对硬币求前缀和,然后推导能成为冠军的即可。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const int N = 1e6 + 7;
int n, tot;
ll prefix[N];
pair<ll, ll> arr[N];
ll ans[N];
signed main() {
    while(~scanf("%d", &n)) {
        tot = 0;

        for(int i = 1; i <= n; ++i) {
            scanf("%lld", &arr[i].first);
            arr[i].second = i;//fi为硬币数,se为编号
        }sort(arr + 1, arr + 1 + n);
        for(int i = 1; i <= n; ++i) {
            prefix[i] = prefix[i-1] + arr[i].first;
        }ans[++tot] = arr[n].second;//最大的一定可以为冠军
        for(int i = n; i >= 2; --i) {
            if(prefix[i-1] >= arr[i].first) //前i-1个的和不小于i的,可以赢
                ans[++tot] = arr[i-1].second;
        }
        sort(ans + 1, ans + 1 + tot);//序号从小到大输出
        printf("%d\n", tot);
        for(int i = 1; i <= tot; ++i) printf("%lld%c", ans[i], i == tot ? '\n' : ' ');
    }
    return 0;
}

C 包裹

  • 题意:给定个包裹的重量,顺序装载,能在天能运完的最小装载量为多少?
  • 明显的二分板子题,装载量满足单调性(小的能装,大的也能),而且给定装载量还容易验证是否可行
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const int N = 1e5 + 7;
ll n, d;
int arr[N];

bool check(int tmp) {
    int index = 1, cnt = 0;//标记当前选取位置、天数
    while(index <= n) {
        ++cnt;
        int tot = 0;
        while(tot + arr[index] <= tmp) {
            tot += arr[index];
            ++index;
            if(index > n) break;
        }
        if(cnt > d) return false;
    }return true;
}

signed main() {
    while(~scanf("%lld%lld", &n, &d)) {
        for(int i = 1; i <= n; ++i) scanf("%d", &arr[i]);
        ll ans = 1, l = 1, r = 0x3f3f3f3f;//[1,INF]中
        while(l <= r) {
            int mid = l + r >> 1;
            if(check(mid)) r = mid - 1, ans = mid;
            else l = mid + 1;
        }printf("%lld\n", ans);
    }
    return 0;
}

D 飞神Ⅱ

  • 题意:给定和7个档位,每个档位第一次有额外奖励,求可获取的最大奖励
  • 首先要注意到每个档位给的基础奖励都是的,所以问题就被大大简化了,只需要注意每个档位的第一次额外奖励是否能拿到即可
  • 判定7个档位中选取若干档位,使得获得的额外奖励最多,可以01背包,也可以二进制枚举,这里就直接枚举了
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 7;
int t, n;
int a[] = {0, 8, 18, 28, 58, 128, 198, 388};//额外奖励
int b[] = {0, 1, 6, 28, 88, 198, 328, 648};//所需代价
signed main() {
    cin >> t;
    while(t--) {
        cin >> n;
        int ans = 0;
        for(int i = 0; i < (1 << 7); ++i) {//2进制枚举,由高位到低位对应7至1档
            int tmp = i;
            int cost = 0, value = 0;
            for(int j = 1; j <= 7; ++j) {
                if(tmp & 1) {
                    cost += b[j];
                    value += a[j];
                }tmp >>= 1;
            }if(cost <= n) ans = max(ans, value + 10 * n);//取最大值
        }cout << ans << endl;
    }
    return 0;
}

F 简单分解

  • 题意:给定,求由,并且其中其中确定的

  • 首先能够比较快想到的是将开始拆,然后继续拆,我们设拆完还剩余的量为,能拆到的最后一个数为

    • ,皆大欢喜,结果就是从乘到,期间取模
    • ,我们就要考虑剩下的放哪些数上更好了,并且还要使得不重复
      • 我们先考虑这个数仅有两项组成:
      • 首先我们比较容易想到的是将直接放到上,很容易就能使得不重复。此时结果为
      • 然后我们再考虑将拆成两部分,分别放到两项中,此时结果为
      • 展开一下,我们就要比较的大小,因为,所以我们只需要比较常数项的大小即可,也就是:,显然,前者比后者更大。又因为常数项前的负号,可知:将拆开放更能得到较大的数
      • 所以得到第一个结论:拆开放比放一起更好。但是我们还需要去找拆开放多少更好,这里可以直接猜每项加,猜不到没关系,我们去打表看
      • 我们取,一共只需要四项。打表代码如下:
#include <bits/stdc++.h>
using namespace std;
signed main() {
    for(int n = 14; n <= 19; ++n) {
        int ans = 0, ta,tb,tc,td;//记录结果,中间项
        for(int a = 2; a <= n; ++a) {
            for(int b = 2; b <= n - a; ++b) {
                if(a == b) continue;//去重
                for(int c = 2; c <= n - a - b; ++c) {
                    if(a == c || b == c) continue;
                    for(int d = 2; d <= n - a - b - c; ++d) {
                        if(a == d || b == d || c == d) continue;
                        if(a * b * c * d > ans) {
                            ans = a * b * c * d;
                            ta = a, tb = b, tc = c, td = d;
                        }
                    }
                }
            }
        }cout << ans << ' ' << ta << ' ' << tb << ' ' << tc << ' ' << td << endl;
    }
    return 0;
}
//结果为:
// 120 2 3 4 5
// 144 2 3 4 6
// 180 2 3 5 6
// 240 2 4 5 6
// 360 3 4 5 6
// 420 3 4 5 7
  • 很明显,从最后一个数开始加1,并且可以循环。那么结果就出来了:从开始拆自然数,然后将余项分别加入每一项中。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e3 + 7, mod = 1e9 + 7;
int n, tot = 0;
int arr[N];//arr[i] = i + 1
signed main() {
    while(~scanf("%d", &n)) {
        if(n <= 4) printf("%d\n", n);
        else {
            tot = 0;
            ll tmp = n, ans = 1;
            int goal = 2;
            while(n - goal >= 0) {
                arr[++tot] = goal;
                n -= goal;
                ++goal;
            }//拆自然数,存入arr中
            for(int i = tot; i >= 1 && n; --i) {
                arr[i] += 1;
                n -= 1;
                if(i == 1) i = tot + 1;//重置循环到最末尾
            }
            for(int i = 1; i <= tot; ++i) ans = arr[i] * ans % mod;
            printf("%lld\n", ans);
        }
    }
    return 0;
}

G 朋友的朋友的朋友?

  • 题意:给定个点,每一个点有权值,有条边,权值为,求联通个点的最小花费
  • 最小生成树板子,多组输入记得初始化(:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e6 + 7, M = 1e6 + 7;//点数,边数

ll n, m, tot = 0 ,cost = 0, fa[N];
int arr[N];

struct G{int from, to , w;}edge[M << 1];//存图
bool cmp(G a, G b) {return a.w < b.w;}

void init(int n) {
    for(int i = 1; i <= n; ++i) {
        fa[i] = i;
    }tot = cost = 0;//注意初始化(:
}

inline ll Find(ll x) {return x == fa[x] ? x : fa[x] = Find(fa[x]);}

void Union(ll x, ll y) {
    x = Find(x), y = Find(y);
    if(x != y) fa[x] = y;
}

bool Kruskal(ll & cost) {//排序的时候记得是排边的数目
    ll cnt = 0;//统计边个数
    cost = 0;
    for(int i = 1; i <= tot; ++i) {
        int u = edge[i].from, v = edge[i].to, w = edge[i].w;
        if(Find(u) != Find(v)) {
            Union(u, v);
            ++cnt;
            cost += w;
            if(cnt >= n-1) break;
        }
    }return (cnt == n - 1);
}

inline ll read() {
    ll s = 0, f = 1;
    char ch;
    do {
        ch = getchar();
        if (ch == '-') f = -1;
    } while (ch < 48 || ch > 57);
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * f;
}

signed main() {
    while(~scanf("%lld%lld", &n, &m)) {
        init(n);
        for(int i = 1; i <= n; ++i) arr[i] = read();
        for(int i = 1; i <= m; ++i) {
            int from = read(), to = read();
            edge[++tot].from = from, edge[tot].to = to, edge[tot].w = arr[from] ^ arr[to];
            edge[++tot].from = to, edge[tot].to = from, edge[tot].w = arr[from] ^ arr[to];
        }sort(edge + 1, edge + 1 + tot, cmp);
        Kruskal(cost);
        printf("%lld\n", cost);
    }
    return 0;
}

E H

  • 摸了,等明天有时间补