zoukankan      html  css  js  c++  java
  • [Ynoi2018]未来日记

    卡常有风险,code需谨慎

    题意:
    一个长为(n)的序列(a),有(m)次操作

    1. 把区间([l,r])内所有x变成y
    2. 查询区间([l,r])内k小值
      ((n leq 100000))

    题解:lxl所谓的“望月悲叹的第一分块”。
    其实就是将数列进行值域分块,同时将数列分块,令 (sum1[i][j])表示前(i)个块里值域在范围(( (j-1)*blocksize, j*blocksize ])的数的个数,(sum2[i][j])表示前(i)个块里值域为(j)的数的个数,空间开的下。
    将在同一个块里的数按顺序从左往右编号,保证权值相同的数编号唯一,同时记录(rid)表示该块中编号对应的权值。

    对于修改操作,先将所有受影响的前缀和操作撤销,对于边角块,暴力重构修改并重新纪录,对于整块分两种情况:若这个块中有权值为(x)的数且没有权值为(y)的数,将它的(rid)存储信息转移到(y)里;若这个块中有权值为(x)的数且有权值为(y)的数,将其暴力重构修改并重新记录,由于一次这样的重构会使这个块内不同权值的数目减(1),所以一个块最多这样重构(sqrt {n})次,将整个序列这样重构时间复杂度为(O(n sqrt {n}))

    对于询问操作,两个边角块另开两个数组(s1[])(s2[]),作用与之前的(sum1[i][j])(sum2[i][j])一样。由于有前缀和操作,所以查询区间任意值域或权值数的个数仅需(O(1))的时间。询问先早答案暴力从小到大枚举在哪一个值域里,然后在值域里暴力枚举答案即可。

    总时间(O(n sqrt {n}))

    代码:

    #include<bits/stdc++.h>
    #pragma GCC optimize(3)//卡常
    using namespace std;
    const int N=110010,BLO=370;
    int n,m,a[N],p,vec[N],v2[BLO];
    int bl[N],L[BLO],R[BLO];
    int s1[BLO][BLO],s2[BLO][N],id[BLO][N],rid[BLO][BLO],pos[N];//此处s1为sum1,s2为sum2
    int sta[20],ttop=0,lp=0;
    char pr[700010];//输出卡常(不会fread)
    
    template <typename T> inline void read(T &x) {
    	x=0;register char flag,c=getchar();while(c<'0'||c>'9')	flag=c,c=getchar();
    	while(c>='0'&&c<='9')	x=x*10+(c^48),c=getchar(); if(flag=='-') x=-x;
    }
    template <typename T> inline void print(T x) {
    	ttop=0; do { sta[++ttop]=(int)(x%10),x/=10; }while(x);
    	while(ttop)	pr[lp++]=sta[ttop--]+'0'; pr[lp++]=10;
    }
    void reset(int x) { for(register int i=L[x];i<=R[x];++i)	a[i]=rid[x][pos[i]]; }
    void change(int bel,int x,int y) {
    	int uid=id[bel][x];id[bel][y]=uid,rid[bel][uid]=y,id[bel][x]=0;
    }
    void build(int x) {
    	for(register int i=1;i<=p;++i)	id[x][rid[x][i]]=0;
    	for(register int i=L[x],idx=0;i<=R[x];++i)
    		if(!id[x][a[i]])
    			id[x][a[i]]=++idx,rid[x][idx]=a[i];
    	for(register int i=L[x];i<=R[x];++i)	pos[i]=id[x][a[i]];
    }
    void rebuild(int l,int x,int y) {
    	for(register int i=bl[l];i<=bl[n];++i) {
    		s2[i][x]+=s2[i-1][x],s2[i][y]+=s2[i-1][y];
    		s1[i][bl[x]]+=s1[i-1][bl[x]],s1[i][bl[y]]+=s1[i-1][bl[y]];
    	}
    }
    void modify(int l,int r,int x,int y) {
    	if(s2[bl[r]][x]-s2[bl[l]-1][x]==0)	return ;
    	for(int i=bl[n];i>=bl[l];--i) {
    		s2[i][x]-=s2[i-1][x],s2[i][y]-=s2[i-1][y];
    		s1[i][bl[x]]-=s1[i-1][bl[x]],s1[i][bl[y]]-=s1[i-1][bl[y]];
    	}
    	if(bl[l]==bl[r]) {
    		reset(bl[l]);
    		for(int i=l;i<=r;++i)
    			if(a[i]==x) {
    				a[i]=y;
    				--s2[bl[l]][x],++s2[bl[l]][y];
    				--s1[bl[l]][bl[x]],++s1[bl[l]][bl[y]];
    			}
    		build(bl[l]),rebuild(l,x,y);return ;
    	}
    	reset(bl[l]),reset(bl[r]);
    	for(int i=l;i<=R[bl[l]];++i)
    		if(a[i]==x) {
    			a[i]=y;
    			--s2[bl[l]][x],++s2[bl[l]][y];
    			--s1[bl[l]][bl[x]],++s1[bl[l]][bl[y]];
    		}
    	for(int i=L[bl[r]];i<=r;++i)
    		if(a[i]==x) {
    			a[i]=y;
    			--s2[bl[r]][x],++s2[bl[r]][y];
    			--s1[bl[r]][bl[x]],++s1[bl[r]][bl[y]];
    		}
    	build(bl[l]),build(bl[r]);
    	for(int i=bl[l]+1;i<bl[r];++i) {
    		if(!s2[i][x])	continue;
    		if(s2[i][y]) {
    			reset(i);
    			for(int j=L[i];j<=R[i];++j)
    				if(a[j]==x) {
    					a[j]=y;
    					--s2[i][x],++s2[i][y];
    					--s1[i][bl[x]],++s1[i][bl[y]];
    				}
    			build(i);
    		}
    		else {
    			s1[i][bl[y]]+=s2[i][x],s1[i][bl[x]]-=s2[i][x];
    			s2[i][y]+=s2[i][x],s2[i][x]=0;
    			change(i,x,y);
    		}
    	}
    	rebuild(l,x,y);
    }
    int ask(int l,int r,int k) {
    	int ans=0,sum=0;
    	if(bl[l]==bl[r]) {
    		reset(bl[l]);for(int i=l;i<=r;++i)	vec[i]=a[i];
    		nth_element(vec+l,vec+l+k-1,vec+r+1),ans=vec[l+k-1];
    		for(int i=l;i<=r;++i)	vec[i]=0; return ans;
    	}
    	reset(bl[l]),reset(bl[r]);
    	for(int i=l;i<=R[bl[l]];++i)	++vec[a[i]],++v2[bl[a[i]]];
    	for(int i=L[bl[r]];i<=r;++i)	++vec[a[i]],++v2[bl[a[i]]];
    	for(int i=1;i<=bl[N-1];++i) {
    		if(sum+v2[i]+s1[bl[r]-1][i]-s1[bl[l]][i]>=k) {
    			for(int j=(i-1)*p+1;j<=i*p;j++) {
    				if(vec[j]+s2[bl[r]-1][j]-s2[bl[l]][j]+sum>=k) { ans=j;goto FLAG; }
    				else	sum+=vec[j]+s2[bl[r]-1][j]-s2[bl[l]][j];
    			}
    		}
    		else	sum+=v2[i]+s1[bl[r]-1][i]-s1[bl[l]][i];
    	}
    	FLAG:
    	for(int i=l;i<=R[bl[l]];++i)	--vec[a[i]],--v2[bl[a[i]]];
    	for(int i=L[bl[r]];i<=r;++i)	--vec[a[i]],--v2[bl[a[i]]];
    	return ans;
    }
    int main() {
    //	int cur=clock();
    	read(n),read(m),p=sqrt(N)+1;
    	for(int i=1;i<N;++i)	bl[i]=(i-1)/p+1;
    	for(int i=1;i<=n;++i)	read(a[i]);
    	for(int i=1;i<=bl[n];++i)
    		L[i]=(i-1)*p+1,R[i]=i*p;R[bl[n]]=n;
    	for(int x=1;x<=bl[n];++x)	build(x);
    	for(int x=1;x<=bl[n];++x) {
    		for(int i=1;i<N;++i)	s2[x][i]=s2[x-1][i];
    		for(int i=1;i<=bl[N-1];++i)	s1[x][i]=s1[x-1][i];
    		for(int i=L[x];i<=R[x];++i)	++s1[x][bl[a[i]]],++s2[x][a[i]];
    	}
    	for(;m;--m) {
    		int opt,x,y;read(opt),read(x),read(y);
    		if(opt==1) { int z,w;read(z),read(w),modify(x,y,z,w); }
    		else { int k;read(k),print(ask(x,y,k)); }
    	}
    	pr[--lp]=''; puts(pr);
    //	printf(">>> %d ms.
    ",clock()-cur);
    	return 0;
    }
    
  • 相关阅读:
    C# 基于密钥的64位加密与解密方法(原创)
    爱情 前途 命运
    设计模式学习笔记装饰模式
    jquery暂无图片插件
    IIS GZIP压缩(转)
    Fckeditor使用笔记
    设计模式学习笔记策略模式
    电子商务网站搜索架构方案
    批量修改数据库表的架构sql
    win 2003 安装 vs2005 sp1 问题1718
  • 原文地址:https://www.cnblogs.com/daniel14311531/p/10197853.html
Copyright © 2011-2022 走看看