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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】