洛谷 P4688 [Ynoi2016]掉进兔子洞 (看到题目背景,galgame玩家狂喜)
作者:互联网
洛谷 P4688 [Ynoi2016]掉进兔子洞
题解背景
我一直以为自己只是凭本能行为的生物,如今,通过和外界的接触,才形成了现在的我
海德格尔在《艺术作品的本源》中写道:
“世界决不是立身于我们面前能让我们细细打量的对象。只要诞生与死亡、祝福与惩罚不断地使我们进
入存在,世界就始终是非对象性的东西,而我们人始终归属于它。”
世界是美丽的,每次从这里眺望街区,都会产生这种的错觉,但是没办法,美丽的事物终归是美丽的,
澄澈透丽的夏日天空,透露一言难以形容的优美
盛开的樱花树下,埋藏着尸体。
樱ノ诗的根基在于梦蝶的虚无。
樱ノ诗与梦蝶。
绽放与虚无。
世界现象的一体两面。
你还真的是个樱一般的艺术家呢!无论多少次,你都能够像是理所当然的风景一般令奇迹之花绽放。正
因为像是理所当然一般的绽放,人们丝毫不能察觉那是奇迹……哪怕花瓣散落……那棵树也会再次开出
花朵……巨大的树干深深地扎根于大地,向着高高的天空不断地生长……所以随着每次的花落,树干会
更粗,树枝会更高,树根会刺入更深的大地。这件事只有圭君知道……所以他才会画出那种画。超越极
限……
题目描述
你有一天正在写题,突然看到了这道题,一看题目背景,素晴日啊!就想
起了还没打完的樱之诗,然后就开始颓,打起了galgame
突然觉得不能这么颓,就写起了题解。
题目分析
针对这类区间询问,而且区间之间可以互相转移的题,很明显是需要用莫队解决的,题目一次给出我们三个区间
我们需要用三个区间的总长度减去这三个区间的交集,首先考虑三个区间求交集怎么做,首先可以暴力维护,用数组存储每一个区间内
每一种颜色出现的次数,然后依次求交即可,但很明显,这种写法无论是时间还是空间都是过不去的,如何快速且方便获取区间交集?
我们发现但观察一下区间特征,假设每个数只出现一次,我们可以发现用bool数组存储+&运算似乎符合求交集的性质,但每个数并非只出
现一次,考虑一个数$ai \(,针对每一个询问区间,我们维护一个bool数组,扫一遍询问区间,\)ai$每在询问出
现一次,我们就把\(ai+cnt-1\)标为1,\(cnt\)为\(ai\)目前出现的次数,这样就可以用bool数组储存询问,利用&就可以求交了,针对这类
询问区间可以互相转移的问题,我们一般采取莫队来求解,\(ai\)的范围过大,我们需要将其离散化,用bool很明显空间还是太大了,所以
就采用bitset来存储。
最终做法就是bitset存储,莫队维护,就可以得到答案了。
我们再观察一下数据范围,发现需要开1e10的bitset,即使是bitset也是
开不下的,所以把询问分组进行了。
其余细节在代码里.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<bitset>
#include<cmath>
#define int long long
using namespace std;
const int maxn=1e5+100;
const int mm=35000+100;
const int mn=35000;
inline int read(){
int ret=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-f;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
ret=ret*10+(ch^'0');
ch=getchar();
}
return f*ret;
}
int n,m;
int a[maxn];
int bl[maxn];
int pos[maxn];
struct node{
int id;
int l,r;
}q[maxn*3+10];
int cnt[maxn];
bitset<maxn> b[mm],bs;
bool vis[mm];
int ans[maxn];
bool cmp(node x,node y){
return pos[x.l]==pos[y.l]?x.r<y.r:pos[x.l]<pos[y.l];//排序函数,与普通莫队的排序函数一样
}
void add(int x){
cnt[a[x]]++;
bs[a[x]+cnt[a[x]]-1]=1;
return ;
}
void del(int x){
cnt[a[x]]--;
bs[a[x]+cnt[a[x]]]=0;
return ;
}
void slove(int k){
memset(vis,false,sizeof(vis));
memset(cnt,0,sizeof(cnt));
memset(ans,0,sizeof(ans));
int tot=0;
for(int i=1;i<=k;i++){
for(int j=1;j<=3;j++){
q[++tot].id=i;//读入
q[tot].l=read();
q[tot].r=read();
// cout<<q[tot].l<<" "<<q[tot].r<<endl;
ans[i]+=q[tot].r-q[tot].l+1;
}
}
bs.reset();
sort(q+1,q+1+tot,cmp);//排序
int l=1,r=0;//初始化左右指针
for(int i=1;i<=tot;i++){//分区间处理
//注意处理顺序,不能让l到r的前面,cnt会变成负数,导致bitset越界
while(r<q[i].r){
add(++r);
}
while(l<q[i].l){
del(l++);
}
while(l>q[i].l){
add(--l);
}
while(r>q[i].r){
del(r--);
}
if(!vis[q[i].id]){
b[q[i].id]=bs;//如果该区间未出现过
vis[q[i].id]=true;
}
else{
b[q[i].id]&=bs;//利用&求交集
}
}
for(int i=1;i<=k;i++){
ans[i]-=b[i].count()*3;//减去交集
}
for(int i=1;i<=k;i++){
cout<<ans[i]<<endl;//输出对应答案
}
return ;
}
signed main(){
// freopen("a.in","r",stdin);
n=read();
m=read();
int si=sqrt(n);
for(int i=1;i<=n;i++){
a[i]=read();
bl[i]=a[i];
}
int nu=ceil((double)n/si);
for(int i=1;i<=nu;i++){
for(int j=(i-1)*si+1;j<=i*si;j++){
pos[j]=i;
if(j>=n)
break;
}
}
/*也可以这样分块
for(int i=1;i<=n;i++){
a[i]=read();
bl[i]=a[i];
pos[i]=(i-1)/si+1;
} */
sort(bl+1,bl+1+n);
for(int i=1;i<=n;i++){
a[i]=lower_bound(bl+1,bl+1+n,a[i])-bl;//注意,不用去重
}
while(114514){//bitset开不下,所以分组执行
if(m<=mn){
slove(m);
break;
}
else{
m-=mn;
slove(mn);
}
}
return 0;
}
完结撒花
说实话,做这道题的原因就是看见了这特殊的题目背景,想写一篇特殊的
题解,既然lxl在题目描述用素晴日,那我就在题解描述
用樱之诗了。
标签:galgame,ch,洛谷,int,Ynoi2016,maxn,区间,bool,include 来源: https://www.cnblogs.com/rpup/p/14022727.html