题解 P4117 【[Ynoi2018]五彩斑斓的世界】

题目链接

我觉得AVX2指令集不够爽,于是写了AVX512.到官网查了一天手册,直接拿下最优解

Solution [Ynoi2018]五彩斑斓的世界

题目大意:给定一个长度为\(n\)的序列.每次将\([l,r]\)内大于\(x\)的数减\(x\).询问\([l,r]\)内\(x\)出现了多少次

分析:还能有啥分析,直接暴力,指令集优化一下就可以了

所有函数都可以在Intel手册查到

首先,既然我们要用指令集,得\(CPU\)资瓷才行

#include <stdint.h>
#include <iostream>
#include <cpuid.h>
static void cpuid(uint32_t func, uint32_t sub, uint32_t data[4]) {
    __cpuid_count(func, sub, data[0], data[1], data[2], data[3]);
}
int main() {
    uint32_t data[4];
    char str[48];
    for(int i = 0; i < 3; ++i) {
        cpuid(0x80000002 + i, 0, data);
        for(int j = 0; j < 4; ++j)
            reinterpret_cast<uint32_t*>(str)[i * 4 + j] = data[j];
    }
    std::cout << str;
}

将如上代码丢入在线\(IDE\),得到评测姬\(CPU\)型号

注意:\(Luogu\)采用集群评测,每次得到的\(CPU\)型号不一定相同.(也就是过不了多交几次的原理?)
在我这儿,得到的型号为:

Intel(R) Xeon(R) Gold 6149 CPU @ 3.10GHz

在\(Intel\)官网是查不到这颗\(CPU\)的信息的,考虑到\(Luogu\)基于阿里云,那这颗\(CPU\)就多半是阿里云定制的.我们可以查询与它规格相近的Xeon(R) Gold 6148
Intel官网查到这颗\(CPU\)支持的指令集:

SSE4.2, AVX, AVX2, AVX-512

你谷非常良心啊,其它\(OJ\)用的都是酷睿,你谷上了至强可扩展……

  • CodeForces: i3-8100
  • LibreOJ: i5-3470
  • bzoj: 想必这OJ的CPU一定古(垃)老(圾),就不查了(而且也查不到)

所以注意了请不要在考场上使用AVX512指令集,用AVX256都是冒险

CCF所采用的i7-8700k仅支持到AVX2,AVX512要等到Cannon Lake去了

关于AMD:她死了

先上代码,然后下文再讲解函数用法:

#pragma GCC target("avx,avx2,avx512f")
#include <immintrin.h>

#include <cstdio>
using namespace std;
const int maxn = 1e5 + 100;
int n,m,val[maxn],*arr;
inline void modify(int *left,int *right,int x){
    __m512i subval = _mm512_set_epi32(x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x);
    while(((long long)(left) % 64) && left <= right){//64位对齐
        if(*left > x)*left -= x;
        left++;
    }
    while(left + 15 <= right){
        __m512i tmp = _mm512_load_epi32(left);
        _mm512_store_epi32(left,_mm512_mask_sub_epi32(tmp,_mm512_cmpgt_epi32_mask(tmp,subval),tmp,subval));//如果tmp对应位大于subval对应位就是两数相减的值,否则不变
        left += 16;
    }
    while(left <= right){
        if(*left > x)*left -= x;
        left++;
    }
}
inline int query(int *left,int *right,int x){
    int ret = 0;
    __m512i sum = _mm512_setzero_epi32(),one = _mm512_set_epi32(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1);
    while(((long long)(left) % 64) && left <= right){//64位对齐
        if(*left == x)ret++;
        left++;
    }
    __m512i tocmp = _mm512_set_epi32(x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x);
    while(left + 15 <= right){
        __m512i tmp = _mm512_load_epi32(left);
        sum = _mm512_mask_add_epi32(sum,_mm512_cmpeq_epi32_mask(tmp,tocmp),sum,one);//如果tmp和tocmp对应位相等,sum对应位+1否则不变
        left += 16;
    }
    while (left <= right){
        if (*left == x) ret++;
        left++;
    }
    _mm512_store_epi32(arr,sum);
    for(int i = 0;i < 16;i++)
        ret += arr[i];
    return ret;
}
int main()
{
    scanf("%d %d",&n,&m);
    arr = new int[128];
    while((long long)(arr) % 64)arr++;//64位对齐
    for(int i = 1;i <= n;i++)val[i] = read();
    for(int i = 1;i <= m;i++){
        int opt,l,r,x;
        scanf("%d %d %d %d",&opt,&l,&r,&x);
        if(opt == 1)modify(val + l,val + r,x);
        else printf("%d\n",query(val + l,val + r,x));
    }
    return 0;
}

从指令集名字我们可以看出来,$AVX512指令集一次性可以处理\(512\)位数据,也就是\(16\)位\(int\)

AVX512指令集的基本数据类型:

__m512,__m512d,__m512i
分别存储单精度浮点数,双精度浮点数,整数

这次我们用__m512i,所以另外两个我们不讲,它可以存储\(64\)个\(char\),\(32\)个\(short\),\(16\)个\(int\),\(8\)个\(long\;long\)

__m512i _mm512_set_epi32 (int e15, int e14, int e13, int e12, int e11, int e10, int e9, int e8, int e7, int e6, int e5, int e4, int e3, int e2, int e1, int e0)

这个函数把\(16\)个\(int\)打包,\(epi32\)显然指的是每个元素的大小(位数),\(epi64\)就适用于\(long\;long\)

__m512i _mm512_load_epi32 (void const* mem_addr)

这个函数从指定内存地址读取\(512\)位,注意mem_addr一定要64位对齐,否则会抛出一般保护性异常

void _mm512_store_epi32 (void* mem_addr, __m512i a)

这个函数将\(a\)中的值存储到\(mem\_addr\)开始的地址中,同样要\(64\)位对齐

__mmask16 _mm512_cmpgt_epi32_mask (__m512i a, __m512i b)

这个函数比较\(a\)和\(b\),返回一个掩码.如果\(a\)对应位$ >$ \(b\)对应位 掩码对应位为\(1\)

__m512i _mm512_mask_add_epi32 (__m512i src, __mmask16 k, __m512i a, __m512i b)

如果写掩码\(k\)对应位为\(0\),那么返回值对应位就是\(src\)的对应位,否则就是\(a\)的对应位加\(b\)的对应位

__m512i _mm512_mask_sub_epi32 (__m512i src, __mmask16 k, __m512i a, __m512i b)

如果写掩码\(k\)对应位为\(0\),那么返回值对应位就是\(src\)的对应位,否则就是\(a\)的对应位减\(b\)的对应位

__mmask16 _mm512_cmpeq_epi32_mask (__m512i a, __m512i b)

这个函数返回一个掩码,如果\(a\)对应位\(=\) \(b\)对应位,掩码对应位为\(1\)

然后记得

#pragma GCC target("avx,avx2,avx512f")
#include <immintrin.h>

没了,基本上就是一个模拟.复杂度\(O(nm/16)\)

跑的挺快的,加个\(mmap\)读入优化轻松冲进2162ms

吸了氧气会变慢就比较迷了,氧气中毒?

原文地址:https://www.cnblogs.com/colazcy/p/11515031.html

时间: 2024-08-29 05:33:28

题解 P4117 【[Ynoi2018]五彩斑斓的世界】的相关文章

【[Ynoi2018]五彩斑斓的世界】

分块毒瘤题.如果真的想好好练习思维以及代码的话就请不要使用\(\text{WC}\)讲过的黑科技指令集. 正文部分: 由乃题必定分块. 我们可以将同一个块内值相同的用并查集维护起来,因为如果是一个块内相同的值打标机怎么改都一样. 接着谈修改,我们设块内最大值为\(\text{mx}\),修改的值为\(\text{v}\) 如果\(v*2>mx\)的话就改大于\(\text{v}\)的,否则就改小于\(\text{v}\)的 原理是因为如果大于\(\text{v}\)的比较少的话改大的,否则改小的

[Ynoi2018]未来日记

"望月悲叹的最初分块" (妈呀这名字好中二啊(谁叫我要用日本轻小说中的东西命名真是作死)) 这里就直接挂csy的题解了,和我的不太一样,但是大概思路还是差不多的,我的做法是和“五彩斑斓的世界”有点类似的维护方法 先考虑如何求区间第k小值.对序列和权值都进行分块,设bi,j表示前j 块中权值在i 块内的数字个数,ci,j 表示前j 块中数字i 的出现次数.那么对于一个询问[l,r] ,首先将零碎部分的贡献加入到临时数组tb 和tc 中,然后枚举答案位于哪一块,确定位于哪一块之后再暴力枚举

优秀数据结构1-分块(v1.0)

---恢复内容开始--- v1.0,征求大佬的修改意见 众所周知,欣隆妹妹是个毒瘤. 经常出毒瘤数据结构题. 出的最多的就是分块. 那么,窝们今天就来学习优(暴)秀(力)数据结构--分块 可爱的由乃. QAQ part-one 什么是分块? 分块,顾名思义,就是把序列分成相等的若干个块,对于每一个块,维护信息,然后再合并. part-two 分块的原理,实现? 现在,我们有一个问题,对一个序列,区间加法,求区间每一个数的和(假设我们不会一种东西叫线段树) 暴力做法: 暴力修改,暴力查询,显然T飞

第1章:我是程序员

青春是什么?也许,青春就是一种回忆,一种奋斗,徘徊在人生道路,也许是迷茫,也许是憧憬,不变的是时间匆匆的流逝...我们慨叹生活,慨叹过去,不经意之间,人生的彷徨,让我们迷失过去,但是,人生,就是一种酸甜苦辣,我们不知道自己会遇到什么,唯有的是努力,努力,还有努力...青春,是你的故事,我的故事,五彩斑斓的世界,青春的梦想....驰骋在我们的内心中...有我们的感动... 第1章 不眠的夜晚----- 莫言,一个菜鸟级别的程序员,此时,已经深夜了...他却睡不着,辗转反侧,程序已经调试很多次了,但

书上的美来自生活

童年,是人生最美好的阶段. 一个人对生活审美感受的培养,最好的阶段,也是在童年.童年的世界,是五彩斑斓的,要投身于这五彩斑斓的世界中,并在心底里,深深地爱上它们.那么,五彩斑斓的光,投射到文字里,就会像梦幻一般,闪烁着童稚而纯朴的美. 也就是说,好的作文,要从生活中来. 这样,我们就必须要有一颗懂得观察生活的心.冰雪的融化,春草的发芽,小虫子的爬行,蚂蚁的决战,云的倏忽飞动,鸟的腾挪跌宕,等等,这一切都要留意观察.还有,雨后的蛙声,晚秋的蝉鸣,高枝上的莺啼,耕作的老牛的哞叫,你们都要留意,去听,

好的作文,要从生活中来

童年,是人生最美好的阶段. 一个人对生活审美感受的培养,最好的阶段,也是在童年.童年的世界,是五彩斑斓的,要投身于这五彩斑斓的世界中,并在心底里,深深地爱上它们.那么,五彩斑斓的光,投射到文字里,就会像梦幻一般,闪烁着童稚而纯朴的美. 也就是说,好的作文,要从生活中来. 这样,我们就必须要有一颗懂得观察生活的心.冰雪的融化,春草的发芽,小虫子的爬行,蚂蚁的决战,云的倏忽飞动,鸟的腾挪跌宕,等等,这一切都要留意观察.还有,雨后的蛙声,晚秋的蝉鸣,高枝上的莺啼,耕作的老牛的哞叫,你们都要留意,去听,

学习SpirngMVC之如何获取请求参数

学习SpringMVC——如何获取请求参数 @RequestParam,你一定见过:@PathVariable,你肯定也知道:@QueryParam,你怎么会不晓得?!还有你熟悉的他(@CookieValue)!她(@ModelAndView)!它(@ModelAttribute)!没错,仅注解这块,spring mvc就为你打开了五彩斑斓的世界.来来来,不要兴(mi)奋(hu),坐下来,我们好好聊聊这么些个注解兄弟们~~~(wait, 都没有听过? 好,来,你坐前排,就你!) 一.spring

java架构解密——Spring框架的AOP

一直以来,我们应用了不少的AOP框架,但是对于AOP的底层实现却没有过多的深入,古话就是"知其然,不知其所以然",随着AOP学术讨论的骤然兴起,我也开拓了自己的眼界,深入了解了AOP这个五彩斑斓的世界! 先来看看大众的定义: 百度百科: 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函

百度发布“无人挖掘机”,吹的牛实现后李彦宏又立了3个flag

导读:AI时代到来了,这个世界会因为AI变得更好吗?YES, AI DO. 来源:百度 01 李彦宏立的3个flag 7月4日百度 AI 开发者大会上,李彦宏说:"曾经吹过的牛实现了,全球首款 L4 级量产自动驾驶巴士'阿波龙'量产下线!" 如今,阿波龙已经安全运营了整整120天. 4个月后的现在,李彦宏在百度世界大会上又立了三个 flag: Flag 1:百度即将发布与一汽红旗合作的 L4 级自动驾驶量产乘用车 百度与一汽红旗合作,共同打造了中国首款 L4 级自动驾驶量产乘用车,并发