zoukankan      html  css  js  c++  java
  • BZOJ2120:数颜色

    浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2120

    对于区间内颜色种类数有一个百用不倦的套路,那就是只有每种颜色只有最左/右边算数,其它都算没颜色。

    这一题也是一样,对于每个位置,我记录一下(nxt[i])表示下一个离(i)最近的颜色与(i)相同的位置在哪。问题就变成了询问在区间([l,r])内,有多少位置的(nxt)是大于(r)的了。用二维线段树就轻松解决了。

    时间复杂度:(O(nlog^2n))

    空间复杂度:(O(nlog^2n))

    代码如下:

    #include <set>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int maxn=5e4+5,maxv=1e6+5;
    
    int n,m;
    char ch[5];
    int a[maxn];
    set<int>s[maxv];
    set<int>::iterator it;
    int lst[maxn],nxt[maxn];
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    struct nxt_segment_tree {
    	int tot;
    	int sum[maxn*100],ls[maxn*100],rs[maxn*100];
    	
    	void change(int p,int l,int r,int pos,int v) {
    		while(1) {
    			sum[p]+=v;
    			if(l==r)break;
    			int mid=(l+r)>>1;
    			if(pos<=mid) {
    				if(!ls[p])ls[p]=++tot;
    				p=ls[p],r=mid;
    			}
    			else {
    				if(!rs[p])rs[p]=++tot;
    				p=rs[p],l=mid+1;
    			}
    		}
    	}
    
    	int query(int p,int l,int r,int pos) {
    		int res=0;
    		while(l!=r) {
    			int mid=(l+r)>>1;
    			if(pos<=mid) {
    				res+=sum[rs[p]];
    				r=mid,p=ls[p];
    			}
    			else l=mid+1,p=rs[p];
    		}
    		return res;
    	}
    }T_inside;
    
    struct pos_segment_tree {
    	int rt[maxn<<2];
    
    	void change(int p,int l,int r,int pos,int v,int opt) {
    		while(1) {
    			if(!rt[p])rt[p]=++T_inside.tot;
    			T_inside.change(rt[p],0,n+1,v,opt);
    			if(l==r)return;int mid=(l+r)>>1;
    			if(pos<=mid)r=mid,p=p<<1;
    			else l=mid+1,p=p<<1|1;
    		}
    	}
    
    	int query(int p,int l,int r,int L,int R,int v) {
    		if(L<=l&&r<=R)return T_inside.query(rt[p],0,n+1,v);
    		int mid=(l+r)>>1,res=0;
    		if(L<=mid)res+=query(p<<1,l,mid,L,R,v);
    		if(R>mid)res+=query(p<<1|1,mid+1,r,L,R,v);
    		return res;
    	}
    }T_outside;
    
    int main() {
    	n=read(),m=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read(),s[a[i]].insert(i);
    	for(int i=1;i<=n;i++)
    		if(!s[a[i]].empty()) {
    			s[a[i]].insert(0);
    			s[a[i]].insert(n+1);
    		}
    	for(int i=1;i<=1000000;i++)
    		for(it=s[i].begin();it!=s[i].end();it++) {
    			int pos=*it;
    			if(pos==0||pos==n+1)continue;
    			if(pos!=0) {it--,lst[pos]=*it,it++;}
    			if(pos!=n+1) {it++,nxt[pos]=*it,it--;}
    			T_outside.change(1,1,n,pos,nxt[pos],1);
    		}
    	for(int i=1;i<=m;i++) {
    		scanf("%s",ch+1);
    		if(ch[1]=='Q') {
    			int l=read(),r=read();
    			printf("%d
    ",T_outside.query(1,1,n,l,r,r));
    		}
    		else {
    			int pos=read(),col=read();
    			if(s[col].empty())s[col].insert(0),s[col].insert(n+1);
    			int x=lst[pos],y=nxt[pos];
    			if(x)T_outside.change(1,1,n,x,pos,-1),T_outside.change(1,1,n,x,y,1);
    			T_outside.change(1,1,n,pos,y,-1);nxt[x]=y,lst[y]=x;
    			it=s[a[pos]].find(pos),s[a[pos]].erase(it);
    			it=s[col].lower_bound(pos),y=*it,it--,x=*it,s[col].insert(pos);
    			if(x)T_outside.change(1,1,n,x,y,-1),T_outside.change(1,1,n,x,pos,1);
    			T_outside.change(1,1,n,pos,y,1);
    			nxt[x]=pos,nxt[pos]=y,lst[y]=pos,lst[pos]=x;
    			a[pos]=col;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    C# 多线程 弹出模态MessageBox的一种方法
    CentOS 7安装Docker
    CentOS 6 安装Docker
    docker三要素
    openstack_dashboard无法获取nova
    cinder安装与配置
    Dashboard安装与配置
    openstack-neutron安装与配置
    openstack在controller节点使用openstack network agent list不显示计算节点
    nova安装与配置
  • 原文地址:https://www.cnblogs.com/AKMer/p/10189008.html
Copyright © 2011-2022 走看看