zoukankan      html  css  js  c++  java
  • 洛谷 P4688 [Ynoi2016]掉进兔子洞 (看到题目背景,galgame玩家狂喜)

    • 洛谷 P4688 [Ynoi2016]掉进兔子洞

    题解背景

    我一直以为自己只是凭本能行为的生物,如今,通过和外界的接触,才形成了现在的我

    海德格尔在《艺术作品的本源》中写道:

    “世界决不是立身于我们面前能让我们细细打量的对象。只要诞生与死亡、祝福与惩罚不断地使我们进

    入存在,世界就始终是非对象性的东西,而我们人始终归属于它。”

    世界是美丽的,每次从这里眺望街区,都会产生这种的错觉,但是没办法,美丽的事物终归是美丽的,

    澄澈透丽的夏日天空,透露一言难以形容的优美

    盛开的樱花树下,埋藏着尸体。

    樱ノ诗的根基在于梦蝶的虚无。

    樱ノ诗与梦蝶。

    绽放与虚无。

    世界现象的一体两面。

    你还真的是个樱一般的艺术家呢!无论多少次,你都能够像是理所当然的风景一般令奇迹之花绽放。正

    因为像是理所当然一般的绽放,人们丝毫不能察觉那是奇迹……哪怕花瓣散落……那棵树也会再次开出

    花朵……巨大的树干深深地扎根于大地,向着高高的天空不断地生长……所以随着每次的花落,树干会

    更粗,树枝会更高,树根会刺入更深的大地。这件事只有圭君知道……所以他才会画出那种画。超越极

    限……

    题目描述

    你有一天正在写题,突然看到了这道题,一看题目背景,素晴日啊!就想

    起了还没打完的樱之诗,然后就开始颓,打起了galgame

    突然觉得不能这么颓,就写起了题解。

    题目链接

    题目分析

    针对这类区间询问,而且区间之间可以互相转移的题,很明显是需要用莫队解决的,题目一次给出我们三个区间

    我们需要用三个区间的总长度减去这三个区间的交集,首先考虑三个区间求交集怎么做,首先可以暴力维护,用数组存储每一个区间内

    每一种颜色出现的次数,然后依次求交即可,但很明显,这种写法无论是时间还是空间都是过不去的,如何快速且方便获取区间交集?

    我们发现但观察一下区间特征,假设每个数只出现一次,我们可以发现用bool数组存储+&运算似乎符合求交集的性质,但每个数并非只出

    现一次,考虑一个数$ai (,针对每一个询问区间,我们维护一个bool数组,扫一遍询问区间,)ai$每在询问出

    现一次,我们就把(ai+cnt-1)标为1,(cnt)(ai)目前出现的次数,这样就可以用bool数组储存询问,利用&就可以求交了,针对这类

    询问区间可以互相转移的问题,我们一般采取莫队来求解,(ai)的范围过大,我们需要将其离散化,用bool很明显空间还是太大了,所以

    就采用bitset来存储。

    最终做法就是bitset存储,莫队维护,就可以得到答案了。

    我们再观察一下数据范围,发现需要开1e10的bitset,即使是bitset也是

    开不下的,所以把询问分组进行了。

    时间复杂度就是普通莫队的时间复杂度

    (O((n+m)sqrt{n}))

    其余细节在代码里.

    #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在题目描述用素晴日,那我就在题解描述

    用樱之诗了。

  • 相关阅读:
    Dynamics CRM开发参考资料
    Sql Server存储过程和游标的配合操作
    C#字符处理的性能问题
    Windows安装完ADFS后卸载ADFS清除ADFS数据库
    C#修改AD账号及密码
    Dynamics CRM安装教程九(续):自建证书的CRM项目客户端设置CRM访问
    Kubernetes实践之深入掌握Pod——Pod健康检查和服务可用性检查
    Kuberbetes实践——镜像拉取策略、command args参数、Pod生命周期和重启策略
    Kubernetes实践之深入掌握Pod——在容器内获取Pod信息
    Kubernetes实践之深入掌握Pod——ConfigMap
  • 原文地址:https://www.cnblogs.com/rpup/p/14022727.html
Copyright © 2011-2022 走看看