提供一种需要卡常的分块写法。
首先看到 \(\operatorname{popcount}\) 操作,便可以自然而然的想到在值域上做文章。
首先因为 \(\operatorname{popcount} \leq \log x\)。
所以可以想到对于一个序列来说,进行一次 \(\operatorname{popcount}\) 操作后至多有 \(\log V\) 个不同的数字。
并且在对这个区间进行全局操作时不同数的数量不增。
因此考虑分块。
块内最开始用 \(tag\) 记录加法操作。
对于块内如果有整块 \(\operatorname{popcount}\) 操作则转换成记录每个值都变成了什么。
由于所有值不大于 \(\log V\),所以量级是 \(\log V\) 的。
类似于阈值分治的思想,不同类型的数据不同处理。
这样做复杂度是 \(O(q \sqrt n \log V)\)。
会超时。
不过还能改进。
这样做在保持分块的情况下不会写线段树的标记只能改进 \(\operatorname{popcount}\)。
但是我们可以用自带的函数。
这样写:
inline int popcount(int n){
return __builtin_popcountll(n);
}
然后考虑一些优化:
记录块内最大值,若为 \(1\) 则跳过 \(\operatorname{popcount}\) 操作。
每次 \(\operatorname{popcount}\) 操作后去重降低后续操作复杂度。
加法操作全部堆到 \(tag\) 里,使得加法操作复杂度稳定在 \(O(\sqrt n)\)。
如此再加上一定卡常就可以过了。
不过这样为什么可以过呢?
因为这个函数内部用自带的表以及二分实现,虽然都是 \(\log\) 量级,但是二分理论上很难跑满。
所以复杂度还是 \(O(q \sqrt n \log V)\),但是常数极小。
以下是比较丑的代码,各位大佬轻喷。
#include<bits/stdc++.h>
#define int long long
#define max(x,y) (x>y?x:y)
using namespace std;
const int maxn = 3e5+1000;
const int maxw = 1000;
int B;
int a[maxn];
int sum;//块的数量
inline int popcount(int n){
return __builtin_popcountll(n);
}
class block{
public:
int l,r;//块的起止位置
int flag;//是否已经被 popcount 过
pair<int,int> chifan[maxw];
int tot;
//popcount 后块内不同元素数量至多 50 个,所以暴力记录查询
int tag;//加法标记
void init();//初始化
void maintain();//散块重构
void change();//popcount 后转换记录方式
void add(int x);
void Pop();
}b[maxw];
inline void block::init(){
flag=0;
tot=0;
tag=0;
}
void block::maintain(){
if(flag==0){
for(int i=l;i<=r;++i) a[i]=a[i]+tag;
tag=0;
}
else{
map<int,int> lwx;
for(int i=1;i<=tot;i++) lwx[chifan[i].first]=chifan[i].second+tag;
for(int i=l;i<=r;++i)
{
a[i]=lwx[a[i]];
}
}
for(int i=1;i<=tot;++i) chifan[i].first=chifan[i].second=0;
flag=tot=tag=0;
}
void block::change(){
for(int i=l;i<=r;++i) a[i]=popcount(a[i]+tag);
tag=0;
flag=1;
map<int,int> use;
for(int i=l;i<=r;++i){
int op=0;
if(use[a[i]]==0)
chifan[++tot]=make_pair(a[i],a[i]),use[a[i]]=1;
}
}
inline void block::add(int x){
tag+=x;
}
void block::Pop(){
if(flag==0) change();
else{
int Maxval=0;
for(int i=1;i<=tot;i++) Maxval=max(Maxval,chifan[i].second+tag);
if(Maxval==1) return ;
for(int i=1;i<=tot;++i)
chifan[i].second=popcount(chifan[i].second+tag);
int f=0;
for(int i=2;i<=tot;i++)
if(chifan[i].first!=chifan[i-1].first) f=1;
if(f==0)
{
for(int i=2;i<=tot;i++) chifan[i].first=chifan[i].second=0;
tot=1;
}
}
tag=0;
}
void changeA(int l,int r,int x){
int bl=l/B+1;
if(l-l/B*B==0) bl--;
int br=r/B+1;
if(r-r/B*B==0) br--;
for(int i=bl+1;i<br;++i)
b[i].add(x);
if(bl!=br){
b[bl].maintain();
b[br].maintain();
for(int i=l;i<=b[bl].r;++i) a[i]+=x;
for(int i=b[br].l;i<=r;++i) a[i]+=x;
}
else{
b[bl].maintain();
for(int i=l;i<=r;i++) a[i]+=x;
}
}
void changeB(int l,int r){
int bl=l/B+1;
if(l-l/B*B==0) bl--;
int br=r/B+1;
if(r-r/B*B==0) br--;
for(int i=bl+1;i<br;++i)
b[i].Pop();
if(bl!=br){
b[bl].maintain();
b[br].maintain();
for(int i=l;i<=b[bl].r;++i) a[i]=popcount(a[i]);
for(int i=b[br].l;i<=r;++i) a[i]=popcount(a[i]);
}
else{
b[bl].maintain();
for(int i=l;i<=r;++i) a[i]=popcount(a[i]);
}
}
int question(int x){
int pos=x/B+1;
if(x-x/B*B==0) pos--;
if(b[pos].flag==0) return a[x]+b[pos].tag;
else{
for(int j=1;j<=b[pos].tot;++j)
if(a[x]==b[pos].chifan[j].first) return b[pos].chifan[j].second+b[pos].tag;
}
}
inline int read(){
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void write(int x)
{
if(x<0)
putchar('-'),x=-x;
if(x>9)
write(x/10);
putchar(x%10+'0');
return;
}
int n,q;
signed main(){
n=read();
q=read();
B=sqrt(n);
for(int i=1;i<=n;i++){
a[i]=read();
}
for(int i=1;i<=n;i=i+B){
b[++sum].l=i;
b[sum].r=min(n,i+B-1);
b[sum].init();
}
for(int i=1;i<=q;i++){
char op;
op=getchar();
while(op<'A'||op>'Z')
op=getchar();
if(op=='A'){
int l,r,x;
l=read();
r=read();
x=read();
changeA(l,r,x);
}
else if(op=='P'){
int l,r;
l=read();
r=read();
changeB(l,r);
}
else{
int x;
x=read();
write(question(x));
putchar('\n');
}
}
return 0;
}
这是一道简单题题目来自:https://leetcode.cn/problems/two-sum/题目给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。提示:22nums.length104−109−109nums[i]109−109−109target109只会存在一个有效答案进阶:你可以想出一个时间复杂度小于O(n2)O(n^2)O(n2)的算法吗?示例1:输入:nums=[2,7,11,15],targe
没有测评,不知道对不对,仅仅过样例而已试题A:日期统计本题总分:5分【问题描述】小蓝现在有一个长度为100的数组,数组中的每个元素的值都在0到9的范围之内。数组中的元素从左至右如下所示:5686916124919823647759503875815861830379270588570991944686338516346707827689565614010094809128502533现在他想要从这个数组中寻找一些满足以下条件的子序列: 1.子序列的长度为8; 2.这个子序列可以按照下标顺序组成一个yyyymmdd格式的日期,并且要求这个日期是2023年中的某一天的日期,例如202309
2023蓝桥C/C++B组省赛文章目录2023蓝桥C/C++B组省赛试题A:日期统计题目描述枚举参考代码试题B:01串的熵题目描述枚举|模拟参考代码试题C:冶炼金属题意描述取交集参考代码试题D:飞机降落题意描述DFS+剪枝,懒得写试题E:接龙数列题意描述DP参考代码试题F:岛屿个数题意描述dfs|连通块参考代码试题G:子串简写题意描述前缀和参考代码试题H:整数删除题意描述双向链表|最小堆参考代码试题I:景区导游题意描述带权LCA参考代码试题J:砍树题意描述树上差分参考代码试题A:日期统计题目描述【问题描述】小蓝现在有一个长度为100的数组,数组中的每个元素的值都在0到9的范围之内。数组中的元素
83.删除排序链表中的重复元素题目描述给定一个已排序的链表的头head,删除所有重复的元素,使每个元素只出现一次。返回已排序的链表。输入:head=[1,1,2]输出:[1,2]解题思路:用一个指向节点类型的指针保存头结点,用另一个指向节点类型的指针对该链表进行遍历,由于是有序的,当出现不同的值就说明不会再出现跟前面的值相同的节点了,最后循环结束的条件是遍历到最后一个节点的时候,也就是该节点的next指向空的时候,停止循环,返回该保存的头结点,另外,如果传过来的头结点是空,则直接返回空。参考代码:/***Definitionforsingly-linkedlist.*structListNod
P1776宝物筛选宝物筛选题目描述终于,破解了千年的难题。小FF找到了王室的宝物室,里面堆满了无数价值连城的宝物。这下小FF可发财了,嘎嘎。但是这里的宝物实在是太多了,小FF的采集车似乎装不下那么多宝物。看来小FF只能含泪舍弃其中的一部分宝物了。小FF对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小FF有一个最大载重为WWW的采集车,洞穴里总共有nnn种宝物,每种宝物的价值为viv_ivi,重量为wiw_iwi,每种宝物有mim_imi件。小FF希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。输入
写在前面以下代码,目前均可通过民间OJ数据(dotcpp&NewOnlineJudge),两个OJ题目互补,能构成全集,可以到对应链接下搜题提交(感谢OJ对题目的支持)如果发现任何问题,包含但不限于算法思路出错、OJ数据弱算法实际超时、存在没考虑到的边界情况等,请及时联系作者题解A.幸运数(模拟)题面题解 由于是填空题,按题意本地暴力,几秒就跑出来结果了,直接交结果代码#includeusingnamespacestd;intans;intmain(){ /* for(inti=1;iB.有奖问答(搜索/dp)题面题解1.搜索:2的30次方种可能,每次要么+10要么清零,遇到100分时
最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理已参加机试人员的实战技巧文章目录最近更新的博客使用说明箱子之形摆放题目输入输出示例一输入输出说明备注Code
华为机试一共三道题,分值分别是100,100,200,满分400分,限时2.5小时。我抽到的这三题相对来说比较简单,满分通过,这里做个总结:第一题:数据分类■ 题目描述 对一个数据a进行分类,分类方法为:此数据a(四个字节大小)的四个字节相加对一个给定的值b取模,如果得到的结果小于一个给定的值c,则数据a为有效类型,其类型为取模的值;如果得到的结果大于或者等于c,则数据a为无效类型。比如一个数据a=0x01010101,b=3,按照分类方法计算(0x01+0x01+0x01+0x01)%3=1,所以如果c=2,则此a为有效类型,其类型为1,如果c=1,则此a为无效类型;又如一个数据a=0x01
只做出来(ACDFGH),挑几个出来,答案不一定正确,但自己测试通过了A、求和求1~20230408的和publicclassMain{ publicstaticvoidmain(String[]args){System.out.println((long)20230409*10115204); }}这里就直接套等差数列的求和公式,答案:204634714038436 D、平均【问题描述】 有一个长度为n的数组(n是10的倍数),每个数Ai都是区间[0,9]中的整数,小明发现数组里每种数出现的次数不太平均,而更改第i个数的代价为bi,他想更改着若干个数的值使得这10种数出现的次数
前言:Hello各位童学大家好!😊😊,茫茫题海你我相遇即是缘分呐,或许日复一日的刷题以及让你疲惫甚至已经厌倦了,但是我们真的真的达到极限了吗?少一点自我感动,没有结果前别太松懈,请相信”一万小时定理“。当你迷茫时抬头看看远方回想当初那个稚嫩脸庞的少年所仰望的目标😇😇,理想主义终需在现实里才能真正实现,接下来让我们静下心来刷题吧,体验学习的快感!🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🏆题目传送门⭐L1-028判断素数(10分)⭐L1-031到底是不是太胖了(10分)⭐L1-025正整数A+B(15分)⭐L1-030一帮一(15分)⭐L1-027出租(20分)⭐L1-032Left-p