NEFU 9.24

A.Modulo Equality

题意

一个数列同时加上一个数,变成另一个数列(两个数列都是mod m),求最小的那个数

思路

刚开始想到一个通解,用差分+KMP直接进行计算,可是出了点差错,浪费了很多时间,后来就直接暴力求解。

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-24 20:59:31
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-24 21:18:29
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\A1.cpp
 */
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5;
struct sa {
    int id;
    int num;
};
bool cmp(const sa& a, const sa& b)
{
    if (a.num != b.num)
        return a.num > b.num;
    return a.id < b.id;
}
sa hh1[N], hh2[N];
map<int, int> map1, map2;
int f(int n, int cnt, int m)
{
    for (int i = 1; i <= cnt; i++) {
        if (map2[(hh1[i].id + n) % m] != hh1[i].num)
            return 0;
    }
    return 1;
}
int n, m;
int a[N], b[N];

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++)
        cin >> b[i];
    for (int i = 1; i <= n; i++)
        map1[a[i]]++;
    for (int i = 1; i <= n; i++)
        map2[b[i]]++;
    int cnt = 0;
    for (auto i : map1)
        hh1[++cnt] = { i.first, i.second };
    cnt = 0;
    for (auto i : map2)
        hh2[++cnt] = { i.first, i.second };
    sort(hh1 + 1, hh1 + 1 + cnt, cmp);
    sort(hh2 + 1, hh2 + 1 + cnt, cmp);
    int wow = hh1[1].num, minx = 0x3f3f3f3f;
    for (int i = 1; i <= cnt; i++) {
        if (hh2[i].num == wow) {
            int ke = (hh2[i].id + m - hh1[1].id) % m;
            // cout << ke << endl;
            if (f(ke, cnt, m))
                minx = min(minx, ke);
        } else
            break;
    }
    cout << minx << endl;
    return 0;
}

B.Maximum Sum on Even Positions

题意

定义一个数组的结果为其下标为偶数的数的和。现在可以让 [ l , r ] [l,r] [l,r]进行反转。求反转一次可以得到的最大结果。

思路

考虑 n = 5 , [ 1 , 4 ] n=5,[1,4] n=5,[1,4]反转:
原来: a [ 0 ] + a [ 2 ] + a [ 4 ] a[0]+a[2]+a[4] a[0]+a[2]+a[4]
反转之后: a [ 0 ] + a [ 1 ] + a [ 3 ] a[0]+a[1]+a[3] a[0]+a[1]+a[3]
差值为 ( a [ 1 ] − a [ 2 ] ) + ( a [ 3 ] − a [ 4 ] ) (a[1]-a[2])+(a[3]-a[4]) (a[1]a[2])+(a[3]a[4])
所以可以把相邻两个看为一个,这题就变成了最大子段和问题,直接dp求解
注意可以1,2和2,3,所以需要两次计算

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-24 21:29:08
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-27 17:37:26
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\B.cpp
 */
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
long long a[N], b[N], c[N];
long long dp1[N], dp2[N];
int t, n;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    while (t--) {
        cin >> n;
        long long ans = 0;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            if (!(i % 2))
                ans += a[i];
        }
        for (int i = 1; i < n; i += 2) {
            b[(i + 1) / 2] = a[i] - a[i - 1];
        }
        for (int i = 2; i < n; i += 2) {
            c[i / 2] = a[i - 1] - a[i];
        }
        dp1[0] = dp2[0] = 0;
        for (int i = 1; i <= n / 2; i++) {
            dp1[i] = max(b[i], b[i] + dp1[i - 1]);
            dp2[i] = max(c[i], c[i] + dp2[i - 1]);
        }
        long long max1 = -1e9, max2 = -1e9;
        for (int i = 1; i <= n / 2; i++) {
            max1 = max(max1, dp1[i]);
            max2 = max(max2, dp2[i]);
        }
        // cout << "ans=" << ans << endl;
        printf("%lld\n", ans + max(0ll, max(max1, max2)));
    }
    return 0;
}

C.Sasha and a Bit of Relax

题意

给定一个含n个整数的序列a,定义 ( l , r ) (l,r) (l,r)是一个funny pair当且仅当r>l,r-l+1为偶数,且a中下标自l到(l+r-1)/2的数的异或和等于下标自(l+r-1)/2+1到r的数的异或和。求序列a中funny pairs的个数

思路

题目给的其实就是有几个连续偶数个序列的异或值为0,直接计算即可

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-27 17:46:57
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-27 17:51:29
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\C.cpp
 */
#include <bits/stdc++.h>
using namespace std;
int n, m;
long long sum = 0, ans = 0;
int a[(1 << 20) + 5][2];
int main()
{
    cin >> n;
    a[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        cin >> m;
        sum ^= m;
        ans += a[sum][i & 1]++;
    }
    cout << ans << endl;
    return 0;
}

E.Walk on Matrix

题意

有这么一个问题:

有一个 n × m n \times m n×m的矩阵 a n , m a_{n, m} an,m,矩阵的每个位置都有一个数,要求寻找一个每次只能向右或向下走的从 ( 1 , 1 ) (1, 1) (1,1) ( n , m ) (n, m) (n,m)的路径,最大化路径上的数的按位与之和。
1 ≤ n , m ≤ 500 , 0 ≤ a i , j ≤ 3 × 1 0 5 1\leq n, m \leq 500,0 \leq a_{i, j} \leq 3 \times 10^5 1n,m500,0ai,j3×105
现在给出解决该问题的一个错误 dp 算法(见题面图片),请构造一组数据,hack 掉这个算法,使得正确答案比错误的输出恰好大k。

思路

首先考虑一下以上的dp错在哪里
我们发现对于AND,也就是按位与操作,不见得一个最大的数字与上某个特定的数字就是最大的,例如:
100000 & 1 = 0 , 1 & 1 = 1
为什么呢?因为不见得大的数字就会和这个特定数字有更多的同为1的二进制位
这个应该很显然
那么我们考虑根据这个东西来Hack他
我们考虑 “骗” 这个dp,让他选择一个看似更大的答案,最后把这个答案给卡掉,就可以让dp的答案为0,最后让这个方案的真正答案为k,差值就是k了。

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-27 17:58:10
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-27 17:59:34
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\E.cpp
 */
#include <bits/stdc++.h>
using namespace std;
int k;
int main()
{
    cin >> k;
    cout << "2 3\n";
    cout << (1 << 17) + k << ' ' << (1 << 17) << " 0\n";
    cout << k << ' ' << k + (1 << 17) << ' ' << k << endl;
    return 0;
}

F.Choosing flowers

题意

有m种物品,每种物品有无限个,你可以购买n个物品。
对于第 i i i种物品:
第一次买时的贡献是 a i a_i ai ,接下来每购买一个的贡献都是 b i b_i bi。即当你买了 x i x_i xi个第 i i i种物品时,贡献是 a i + b i × ( x i − 1 ) a_i+b_i \times (x_i-1) ai+bi×(xi1)
现在要你求出最大贡献。

思路

首先很显然的,我们把每一种物品拆成 2 2 2个,一个贡献是 a i a_i ai,一个贡献是 b i b_i bi。然后按贡献排序(显然尽量取贡献大的)。
我们再设 s o l v e ( t , n n ) solve(t,nn) solve(t,nn)表示从下标为 t t t的物品往后取,还能取 n n nn nn个。在遍历的时候分 3 3 3种情况。

如果 i i i是第一次买的贡献,那很显然 a n s ans ans直接加贡献, n n nn nn减一。
如果 i i i是第二次买的贡献且第一次买过了,那剩下 n n nn nn个肯定都买这个了。
如果 i i i是第二次买的贡献且第一次没有买,那么我们先算买这种的贡献,然后和不买这种的(即 s o l v e ( i + 1 , n n ) solve(i+1,nn) solve(i+1,nn))取最大值。
具体第一次是否买过了可以用数组标记。

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-27 18:08:15
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-27 18:26:11
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\F.cpp
 */
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
#define ll long long
struct node {
    ll x;
    int y, id;
} p[N * 2];
int t, m, tot;
ll n, a[N][2];
bool flag[N];
bool cmp(const node& a, const node& b) { return a.x > b.x; }
ll solve(int t, int nn)
{
    ll res = 0;
    for (int i = t; i <= tot && nn; i++) {
        if (p[i].id == 0) {
            res += p[i].x;
            nn--;
            flag[p[i].y] = 1;
        } else {
            if (flag[p[i].y]) {
                res += nn * p[i].x;
                break;
            } else {
                ll val = a[p[i].y][0] + (nn - 1) * p[i].x;
                res += max(val, solve(i + 1, nn));
                break;
            }
        }
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    while (t--) {
        tot = 0;
        cin >> n >> m;
        for (int i = 1; i <= m; i++) {
            cin >> a[i][0] >> a[i][1];
            flag[i] = 0;
            p[++tot] = (node) { a[i][0], i, 0 };
            p[++tot] = (node) { a[i][1], i, 1 };
        }
        sort(p + 1, p + 1 + tot, cmp);
        cout << solve(1, n) << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值