P5356 [Ynoi2017] 由乃打扑克
md调了5h才调出来恶心坏了没想到这么快就做了第二道Ynoi
据说这题其实不卡常
题面也很清楚,给定两种操作,一种是区间加,一种是询问区间内第 k 小的数的值是多少。
对于区间加,在分块入门系列里面是直接对于修改过的散块进行重排,剩下的直接用 tag 来标记,我也是这么想的,所以我试了一下:
我觉得我写的没有问题,一定是这样做不对所以我换了个思路,直接将当前散块内的整块里的数分为两部分,修改的和没修改的,然后直接暴力修改,用两个队列把两种数给存下来,然后枚举重排,把两个队列里的元素每次都取小的插入排完序的数组内,因为遍历的是有序的数组,所以两个队列里面先进的数肯定比后面的小,这样就解决了区间加的问题。
然后来考虑区间kth,我们不难想到可以二分第 k 小的数的值,然后我们就可以在写个查询区间内小于等于 mid 的数个个数,对于整块,我们再进行二分查找(也可以重载运算符用upper_bound但是我不太会重载,所以没写),然后散块直接暴力。
还有一些小优化,比如如果要是当前的 k 是 1 的话就是直接找最小值,如果是区间长度的话就是直接找区间内最大值,在查询函数二分的时候也是同理,如果最大值都小于 mid 那么可以直接加上块长,如果最小值都大于 mid 可以直接跳过。
code:
#include<bits/stdc++.h>
#define int long long
#define N 300100
using namespace std;
int n,m,b[N],kc,bl[N],tag[N],block;
struct sb{int num,id;}e[N],pp[2100],qq[2100];
inline bool cmp(sb a,sb b){return a.num<b.num;}
inline int read(){int x=0;int f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}return x*f;}
inline void print(int x){if(x>=10)print(x/10);putchar(x%10+48);}
inline void add(int l,int r,int c)
{
int c1=0,c2=0;
for(int i=(bl[l]-1)*kc+1;i<=bl[l]*kc;i++)
{
if(i>=l&&i<=r)b[i]+=c;//暴力修改原数列
if(e[i].id>=l&&e[i].id<=r)//在区间范围内
qq[++c2]=(sb){e[i].num+c,e[i].id};//用qq存下更新后的
else pp[++c1]=e[i];//否则保持不变
}
int k1=1,k2=1,t=(bl[l]-1)*kc+1;//开始合并
while(t<=bl[l]*kc)
{
if(k1<=c1&&(k2>c2||pp[k1].num<qq[k2].num))e[t++]=pp[k1],k1++;//只要pp还有数,并且比当前的qq要小,就放pp
else e[t++]=qq[k2],k2++;//反之放qq
}
if(bl[l]!=bl[r])//lr不在同一块里的情况
{
c1=0;c2=0;//照常处理
for(int i=(bl[r]-1)*kc+1;i<=bl[r]*kc;i++)
{
if(i>=l&&i<=r)b[i]+=c;
if(e[i].id>=l&&e[i].id<=r)
qq[++c2]=(sb){e[i].num+c,e[i].id};
else pp[++c1]=e[i];
}
k1=1,k2=1,t=(bl[r]-1)*kc+1;
while(t<=bl[r]*kc)
{
if(k1<=c1&&(k2>c2||pp[k1].num<qq[k2].num))e[t++]=pp[k1],k1++;
else e[t++]=qq[k2],k2++;
}
}
for(int i=bl[l]+1;i<bl[r];i++)tag[i]+=c;//直接修改标记
return ;
}
/*inline void add(int l,int r,int c)
{
for(int i=l;i<=min(kc*bl[l],r);i++)b[i]+=c;
for(int i=(bl[l]-1)*kc+1;i<=kc*bl[l];i++)e[i].num=b[i],e[i].id=i;
sort(e+(bl[l]-1)*kc+1,e+kc*bl[l]+1,cmp);
if(bl[l]!=bl[r])
{
for(int i=(bl[r]-1)*kc+1;i<=r;i++)b[i]+=c;
for(int i=(bl[r]-1)*kc+1;i<=min(r,kc*bl[r]);i++)e[i].num=b[i],e[i].id=i;
sort(e+(bl[r]-1)*kc+1,e+min(r,kc*bl[r])+1,cmp);
}
for(int i=bl[l]+1;i<bl[r];i++)tag[i]+=c;
return ;
}*/
inline int ask(int l,int r,int k)//查询序列里面有多少数是小于等于k的
{
int res=0;
for(int i=l;i<=min(bl[l]*kc,r);i++)
if(b[i]+tag[bl[l]]<=k)res++;//暴力查询左散块
if(bl[l]!=bl[r])
for(int i=(bl[r]-1)*kc+1;i<=r;i++)
if(b[i]+tag[bl[r]]<=k)res++;//暴力查询右散块
for(int i=bl[l]+1;i<bl[r];i++)//整块
{
int nl=(i-1)*kc+1,nr=i*kc;//左右边界
if(e[nr].num+tag[i]<=k)//如果要是最大值就比k小
{
res+=nr-nl+1;//直接加块长跳过
continue;
}
if(e[nl].num+tag[i]>k)continue;//最小值都比k大直接跳过
while(nl<nr)//二分查找
{
int mid=(nr+nl)/2+1;
// cout<<mid<<endl;
if(e[mid].num+tag[i]<=k)nl=mid;
else nr=mid-1;
}
if(e[nl].num+tag[i]<=k)res+=nl-(i-1)*kc;//累加小于等于k的数的个数
}
return res;
}
signed main()
{
// freopen("55.in","r",stdin);
// freopen("552.out","w",stdout);
n=read();m=read();
kc=1000;//求出块长
for(int i=1;i<=n;i++)
{
b[i]=e[i].num=read();//存入两个数组
bl[i]=(i-1)/kc+1;//标记当前数属于哪一块
e[i].id=i;//存编号
}
// cout<<"cao"<<endl;
for(int i=1;i<=n;)//从一到n遍历每一个数
{
int j;
for(j=i;bl[j]==bl[i];j++);//只要他俩相等就一直j++;
sort(e+i,e+j,cmp);//最后j的值是当前块多一,所以不用加一
i=j;//替换i值
}
for(int i=1;i<=m;i++)//遍历询问
{
// for(int j=1;j<=n;j++)
// cout<<b[j]<<endl;
// cout<<endl;
int op=read(),l=read(),r=read(),k=read();
if(op==1)//查询操作
{
// for(int j=1;j<=n;j++)
// cout<<tag[bl[j]]<<" ";
// cout<<endl;
int nl=2e9,nr=-2e9;//左右边界极限值
for(int j=l;j<=min(r,bl[l]*kc);j++)//处理左边散块,有可能lr在同一块
{
// cout<<j<<endl;
if(nl>b[j]+tag[bl[l]])nl=b[j]+tag[bl[l]];//对于左端点取最小值
if(nr<b[j]+tag[bl[l]])nr=b[j]+tag[bl[l]];//对于右端点取最大值
}
// cout<<l<<' '<<r<<endl;
// cout<<bl[l]<<" "<<bl[r]<<endl;
if(bl[l]!=bl[r])//不在同一块里
{
for(int j=(bl[r]-1)*kc+1;j<=r;j++)//同理处理nlnr的值
{
// cout<<j<<endl;
if(nl>b[j]+tag[bl[r]])nl=b[j]+tag[bl[r]];
if(nr<b[j]+tag[bl[r]])nr=b[j]+tag[bl[r]];
}
}
for(int j=bl[l]+1;j<bl[r];j++)//处理中间的整块
{
// cout<<"cao"<<endl;
// cout<<"G:"<<e[(j-1)*kc+1].num+tag[j]<<' '<<e[j*kc].num+tag[j]<<endl;
if(nl>e[(j-1)*kc+1].num+tag[j])nl=e[(j-1)*kc+1].num+tag[j];//因为e里面的数都是从小到大排好的
if(nr<e[j*kc].num+tag[j])nr=e[j*kc].num+tag[j];//同上直接找最大值
}
if(k==1)//等于1的情况
{
cout<<nl<<'\n';
continue;
}
if(k==nr-nl+1)//最大值的情况
{
cout<<nr<<'\n';
continue;
}
int ans=-1;
// cout<<"G:"<<nl<<" "<<nr<<endl;
while(nl<=nr)//二分第k小的值
{
// cout<<nl<<" "<<nr<<endl;
int mid=(nr+nl)/2;
// cout<<mid<<endl;
int cao=ask(l,r,mid);
// cout<<"cao:"<<cao<<endl;
if(cao<k)nl=mid+1;
else ans=mid,nr=mid-1;
}
cout<<ans<<'\n';//输出答案
}
else add(l,r,k);
}
return 0;
}
/*
5 3
1 5 4 2 3
1 2 4 2
2 1 4 4
1 2 4 3
4
-1
10 10
15 11 -18 12 6 9 14 -2 -10 6
1 2 3 1
2 2 4 -3
1 4 10 7
1 2 2 1
1 8 8 1
2 4 10 4
1 4 10 1
1 7 10 4
2 1 4 -5
1 1 8 4
*/
【龙门粗口】
P5356 [Ynoi2017] 由乃打扑克的更多相关文章
- 洛谷P5356 [Ynoi2017] 由乃打扑克
题目 https://www.luogu.com.cn/problem/P5356 思路 由乃题,那么考虑分块(大雾,但确实分块是正解). 题面很清晰,就是求动态的区间第k小,支持区间加法操作. 根据 ...
- [bzoj4763]雪辉&[bzoj4812][Ynoi2017]由乃打扑克
来自FallDream的博客,未经允许,请勿转载,谢谢. cut掉部分题面. 给一个n个点的树,点有点权,有m次询问,每次询问多条链的并有多少种不同的点权以及它的mex mex就是一个集合中最小的没有 ...
- bzoj4812: [Ynoi2017]由乃打扑克
由于查询的是树链的并的信息,同时信息不能高效合并,只能考虑用bitset维护,小范围暴力预处理以便从bitset算出答案 对树分块,保证每块是连通的且直径较小,对分出的块缩点建新树,在新树上建树上ST ...
- 洛谷P3603 || bzoj 4763 雪辉 && bzoj4812: [Ynoi2017]由乃打扑克
https://www.luogu.org/problemnew/show/P3603 https://www.lydsy.com/JudgeOnline/problem.php?id=4763 就是 ...
- java和h5 canvas德州扑克开发中(二)
德州扑克网页源码在github上分享 https://github.com/lxr1907/pokers 感兴趣的可以上去看下. 1.通讯使用websocket,主要在message.js中. 2.用 ...
- java和h5 canvas德州扑克开发中(一)
先附上我的德州扑克测试地址 http://120.26.217.116:8080/LxrTexas/texasIndex.html 我和一个朋友的德州扑克历时一个多月开发,目前已经基本可玩. 前端主要 ...
- 德州扑克AI WEB版
继续之前的德州扑克话题,上次的DOS界面确实没法看,我女朋友说这是什么鬼.哈哈,估计只有自己能玩了 这两天重构了一下界面,基于web服务器和浏览器来交互. 服务器和客户端之间用websocket通信, ...
- 德州扑克AI实现 TexasHoldem Poker
参考了一下这篇文献,http://cowboyprogramming.com/2007/01/04/programming-poker-ai/ 自己用go实现了一个德州扑克AI,效果还可以. 正常和它 ...
- 第一篇代码 嗨翻C语言 21点扑克
/* * 计算牌面点数的程序. * 使用“拉斯难加斯公开许可证”. * 学院21点扑克游戏小组. */#include <stdio.h>#include <stdlib.h& ...
- [swustoj 1088] 德州扑克
德州扑克(1088) 问题描述 德州扑克是一款风靡全球的扑克游戏.德州扑克一共有52张牌,没有王牌.每个玩家分两张牌作为“底牌”,五张由荷官陆续朝上发出的作为公共牌.开始的时候,每个玩家会有两张面朝下 ...
随机推荐
- 全新TI AM62xx系列核心板上市,小小身板蕴藏巨大势能!
2011年TI推出AM335x,成为了此后市场上最受欢迎的通用工业级ARM处理器,并广泛应用于工业HMI, 医疗电子,机器人,能源,汽车等领域.随着工业4.0的发展,HMI人机交互.工业工控.医疗等领 ...
- VScode_Keter_自用
1.软件下载 支持win/linux/mac平台,安装有两种形式,根据个人喜好进行选择即可: 1.安装包 a.官网下载地址:https://code.visualstudio.com/ (速度慢) b ...
- PHP文件及运行(适合PHP初学者)
PHP文件可包含HTML.JavaScript代码和 PHP代码,换句话说PHP 代码可以嵌入HTML文档.PHP文件名以php为后缀. PHP代码以"<?php"开头,以& ...
- Android笔记--视图显示
视图显示 视图的宽高设置 方式一:在.xml文件中设置视图的宽和高 通过调用android:layout_width设置视图的宽 通过调用android:layout_height设置视图的高 宽和高 ...
- 制作微软原版Windows11 PE(含Powershell)
1.adksetup下载链接:https://download.microsoft.com/download/1/f/d/1fd2291e-c0e9-4ae0-beae-fbbe0fe41a5a/ad ...
- 一个基于GPT模型实现的Git Commit信息自动生成工具
每次提交代码的时候,你是否有为如何写Commit Message而迟迟按不下提交的时刻呢?然后,死磨硬泡写了一些并提交后,又被review的小伙伴吐槽了呢?相信很多小伙伴有过这样的经历吧? 趁着最近C ...
- 第三届材料化学与复合材料国际学术会议(MCCM 2022)
大会官网:http://www.meeting-mccm.org/ 大会时间:2022年12月16-18日 大会地点:中国-珠海 截稿日期:详情见官网(2022年10月14日) 接受/拒稿通知:投稿后 ...
- react抽离配置文件、配置@符号、调整src文件夹---配置scss、编写项目的页面结构、创建各个页面 src/views、开始路由、入口文件处修改代码、修改App.js布局文件、添加底部的导航布局、构建个人中心。。。声明式跳转路由、使用React UI库请求渲染首页数据、
1.回顾 2.react项目的配置 react默认创建的项目配置文件在 node_modules/react-scripts 文件夹内部 2.1 抽离配置文件 cnpm run eject cnpm ...
- stm32的学习笔记1
一 目录结构管理 Libraries是放官方固件库的 MDK-ARM是放产生的文件的,工程存放的目录 USERS是放自己写的代码的 然后是一个解释文件README 在MDK-ARM目录里还要创建两个文 ...
- 【CTF】系统调用号查询表
32位 #ifndef _ASM_X86_UNISTD_32_H #define _ASM_X86_UNISTD_32_H 1 #define __NR_restart_syscall 0 #defi ...