学习记录 题解

牛客萌新赛6 --- A题


首先考虑单个数异或最大值,易知x与该数的二进制每一位上都不同。
考虑区间大于等于2,x的二进制的每一位应是要尽量与区间上的数的二进制对应位上的不同。
所有我们只要统计区间上每一位为1的数量,与0的数量进行比较,若1多则该位为0,否则为1。
因为题目要求最小解,所以相等时也为0。

int cnt[N][32];

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        //统计每一位上1的个数
        int m = a[i];
        for (int j = 1; j < 32; j++) cnt[i][j] = cnt[i - 1][j];
        int t = 1;
        while(m) {      
            cnt[i][t] += m % 2;
            m /= 2; 
            t++;
        }
    }
    int q;
    cin >> q;
    while(q--) {
        int l, r;
        cin >> l >> r;
        int len = r - l + 1;
        string s = "";
        for (int i = 1; i < 32; i++) { 
            if(cnt[r][i] - cnt[l - 1][i] > (len - 1) / 2) { //1的个数>=0的个数
                s += '0';
            }else {
                s += '1';
            }
        }
        reverse(s.begin(), s.end()); //上面是由低到高位,需要逆转
        // ll sum = 0;
        // for (int i = 0; i < 31; i++) {
        //     if(s[i] == '1') {
        //         sum += pow(2, 31 - i - 1);
        //     }
        // }
        // cout << sum << "\n";
        bitset<31> t(s);    
        cout<<t.to_ullong()<<'\n';
    }
}
牛客萌新赛6 --- H题

这题虽说没什么难度,但是好久没写dp了,一时兴起,想记录一下。
dp[i][j]:在第j个柱子的第i个单位高度上获得的最大分数。
ret[i]:在第i个单位高度上的最大分数
两种操作:
1.在当前柱子上向上走1个单位高度,不获得分数。
2.往上跳k个单位高度,可以跳到任意柱子上,获得跳到的位置的分数
易得转移方程:dp[i][j] = {dp[i][j], dp[i - 1][j], ret[i - k] + w[i][j]}。

int w[10005][1005];
int dp[10005][1005];

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> w[i][j];
        }
    }
    vector<int> ret(n + 1);
    for (int i = 1; i <= m; i++) {
        dp[1][i] = w[1][i];
        ret[1] = max(dp[1][i], ret[1]);
    }
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if(i - k >= 1) {
                ret[i - k] = max(ret[i - k], dp[i - k][j]);
                dp[i][j] = max(dp[i][j], ret[i - k] + w[i][j]);
            }
            dp[i][j] = max(dp[i][j], dp[i - 1][j]);
            ret[i] = max(ret[i], dp[i][j]);
        }
    }
    // for (int i = 1; i <= n; i++) {
    //     for (int j = 1; j <= m; j++) {
    //         cout << dp[i][j] << ' ';
    //     }cout << "\n";
    // }

    int ans = 0;
    for (int i = 1; i <= m; i++) {
        ans = max(ans, dp[n][i]);
    }
    cout << ans << endl;
}
牛客萌新赛6 --- L题


题目大意:给你n,x,一个长度为n的数组a,好区间:左右端点l,r满足a[l]+..+a[r]>=x;
问有多少个好区间。(1 <= n <= 2e5, -1e9 <= a[i], x <= 1e9)

先前缀和一下,好区间:sum[r] - sum[l - 1] >= x, 移项得sum[r] - x >= sum[l - 1]。
思路:枚举右端点,找满足条件的左端点个数。
做法:用树状数组维护满足条件的左端点个数,由于前缀和可能为负或值过大,所以还需要离散化。

ll a[N];
ll t[N * 2];

int lowbit(int x) {
    return x & (-x);
}

void add(int p) {
    for (int i = p; i <= 2e5 + 10; i += lowbit(i)) {
        t[i]++;
    }
}

ll qy(int p) {
    ll sum = 0;
    for (int i = p; i; i -= lowbit(i)) {
        sum += t[i];
    }
     return sum;
}

void solve() {
    int n, m;
    cin >> n >> m;
    vector<ll> pos;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        a[i] += a[i - 1];
        pos.push_back(a[i]);
    }
    //离散化
    sort(pos.begin(), pos.end());
    pos.erase(unique(pos.begin(), pos.end()), pos.end());
    //
    ll ans = 0;
    for (int i = 1; i <= n; i++) {  //从左往右枚举
        if(a[i] >= m) { //1~i满足条件
            ans++;
        }
        //在离散化数组中找第一个大于a[i]-m的,再-1就是满足条件的
        int l = upper_bound(pos.begin(), pos.end(), a[i] - m) - pos.begin() - 1;
        if(l >= 0) {  //若合法,则在树状数组中求<=a[i]-m的个数
            ans += qy(l + 1);
        }
        //将该点添进树状数组中去
        l = lower_bound(pos.begin(), pos.end(), a[i]) - pos.begin() + 1;
        add(l);
    }
    cout << ans << endl;
}
B. The Walkway

题目大意:给定n,m,d,对应n个位置,m个卖饼人的位置,吃了饼后走d个位置就需要再吃一个饼,
走到卖饼人的位置时需要吃一个饼。现在可以删除一个卖饼的人,问最少要吃多少饼,及可以达到该最小值删除卖饼人的方案数。
我们只需要枚举每一个卖饼人的位置,计算删掉其的贡献,取最小值,并统计方案数即可。

void solve() {
    int n, m, d;
    cin >> n >> m >> d;
    vector<int> id(m + 2);
    for (int i = 1; i <= m; i++) {
        cin >> id[i];
    }
    id[0] = -d + 1;   //边界,因为在第1位置时必须吃
    id[m + 1] = n + 1;
    
    int sum = 0;
    for (int i = 1; i <= m + 1; i++) {
        sum += (id[i] - id[i - 1] - 1) / d;  //计算每个卖饼的人的中间的贡献
        //不算卖饼的人的全部贡献
    }
    
    int ans = n + 1;
    int cnt = 0;
    for (int i = 1; i <= m; i++) {
        int ret = sum;
        //计算删去第i个卖饼的人产生多少贡献
        ret -= (id[i] - id[i - 1] - 1) / d;   //
        ret -= (id[i + 1] - id[i] - 1) / d;   //减去i~i-1和i+1~i提供的贡献
        ret += (id[i + 1] - id[i - 1] - 1) / d; //加上i+1~i-1提供的贡献
        ret += m - 1;     //加上有多少个卖饼的人-1
        if(ans > ret) {
            ans = ret;
            cnt = 1;
        }
        else if(ans == ret) {
            cnt++;
        }
    }
    cout << ans << ' ' << cnt << "\n";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值