Eating

题目链接
题意:
给定一排史莱姆和一系列新史莱姆的重量,计算每次查询中新史莱姆能吃掉多少个左侧的史莱姆。吃掉条件是新史莱姆的重量大于或等于左侧史莱姆的重量,吃掉后的新重量为两者的异或值。
思路:
首先了解一个概念,MSB表示一个二进制数中最高位的1所在的位置。如果吃掉一个MSB小于x的史莱姆x的MSB不会减少,如果吃掉一个MSB等于x的史莱姆,x的MSB会减少。MSB大于x的史莱姆x吃不掉。所以每一次我们找到最后一个MSB大于等于x的MSB的位置,那么此位置之后的所以史莱姆全部都可吃,然后再检查该位置可不可吃即可。具体实现见代码。

点击查看代码
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define ll long long 
const int N = 2e5+5;
int w[N],pre[N];
int dp[N][30]; //dp[i][j]表示前i个数中最高位(二进制形式)大于等于j最靠后的位置.
void solve(){
    int n,q;
    cin>>n>>q;
    pre[0]=0;
    for(int i=1;i<=n;++i){
        cin>>w[i];  //读入权值
        pre[i]=pre[i-1]^w[i]; //前缀异或
    }
    for(int i=0;i<=n;++i){
        for(int j=0;j<30;++j){
            dp[i][j]=-1; //全部初始化为-1
        }
    }
    for(int i=1;i<=n;++i){
        int x=__lg(w[i]); //x表示w[i]的最高位(二进制)的索引 
        for(int j=0;j<=x;++j){ //根据定义,此范围内的dp[i][j]值均为i
            dp[i][j]=i;
        }
        for(int j=x+1;j<30;++j){  //其余范围内的dp[i][j]继承前一个
            dp[i][j]=dp[i-1][j];
        }
    }
    while(q--){
        int x;
        cin>>x; 
        int r=n; //没被吃掉的最右端
        while(1){
            if(r<1) break; //说明全部吃了,退出
            int idx=dp[r][__lg(x)]; //idx表示在没被吃掉的范围中最高有效位大于或等于x的最靠后的位置
            if(idx==-1){ //说明没有被吃掉的范围中最高有效位都小于x,说明全部可以吃掉
                r=0;
                break;
            }
            x=x^pre[r]^pre[idx]; //在idx+1~r的范围都可以吃,x随之更新
            r=idx; //没被吃的最右端随之变成idx
            if(x<w[idx])break; //说明这个啃不下了,退出循环
            x=x^w[idx]; //说明吃了,x和r都更新
            r--;
        }
        cout<<n-r<<" "; //输出吃了多少个
    }
    cout<<endl;
}
signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int _=1;
    cin>>_;
    while(_--)
    solve();
    return 0;
}
posted @   sjgigj  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示