zoukankan      html  css  js  c++  java
  • 【2016北京集训】数组

    Portal --> broken qwq

    Description

    ​  给你一个数组,每个元素有一个颜色,要求支持两种操作:

    1、修改某个元素的颜色

    2、询问这个数组有多少个自取件内没有重复的颜色

    ​​  数据范围:(n<=10^5,m<=2n),颜色大小在(1sim n)之间

    ​  

    Solution

    ​​  这题。。本来应该是一个树套树题

    ​​  但是为什么一定要用树套树呢对吧qwq

    ​​  首先是套路:考虑维护一个(pre)数组,表示每个节点的前一个最近的和它颜色一样的节点,那么我们考虑固定一个右端点,将左端点往左移,直到再往前移一位就会导致当前区间内的(pre)的最大值在这个区间内,这个时候区间的长度就是这个右端点对答案的贡献

    ​  然而。。直接这样做是不行的因为我们要支持修改和多组询问qwq

    ​  所以这里考虑用线段树维护一个神秘的东西:对于线段树中的一个节点,假设它对应的区间是([l,r]),那么我们维护一个(mx)(ans),分别表示当前区间内(pre)的最大值,以及,只考虑当前区间的(pre)限制的右端点贡献之和(这个概念描述起来有点神秘,具体一点就是:首先这个(ans)记录的是该区间中的每个点作为右端点算得的贡献之和,但是这个贡献在计算的时候,只考虑当前区间内的(pre)的影响,更加直观一点来说就是可以理解为移动左端点求(pre)的最大值的时候,如果说新加进来的位置不在([l,r])区间内,就不取(max)),然后答案就应该是线段树根节点的(ans)值了,对于叶子节点来说(ans=x-pre[x]),其中(x)是这个叶子节点在数组中对应的位置

    ​  现在考虑怎么维护这个东西

    ​  为了方便接下来的描述,约定用(x)表示当前区间,(L)表示当前区间的左儿子(左半部分),(R)表示当前区间的右儿子(右半部分),然后我们考虑怎么用(ans[L])(ans[R])求得(ans[x]),这里需要根据(mx[L])(mx[R])的大小关系进行一些讨论,首先我们先看最简单的情况:

    (1)如果说(mx[L]>=mx[R]):首先(ans[L])肯定还是会作为(ans[x])的一部分的,因为加入的东西在后面,不会影响(ans[L])的贡献,然后我们看([mid+1,r])区间中的元素,因为(mx[R]<=mx[L]),也就是说后面的元素作为右端点的时候左端点停下的地方肯定在(mx[L]+1)这个位置,所以我们可以直接计算贡献:(ans[x]=ans[L]+(mid-mx[L])*(r-mid)+sumlimits_{i=1}^{r-mid}i),具体一点的话就是([mid+1,r])中的每个元素对应的左端点可以先走到(mid+1)这个位置,再走到(mx[L]+1)的位置

    ​  

    ​​  接下来看复杂一点的另一种情况:

    (2)如果说(mx[L]<mx[R]),那么说明([mid+1,r])中有一部分的元素对应的左端点可以走到(mx[L]+1),有的在([mid+1,r])中的某个位置就停下了,这个时候我们就不能直接计算答案了,由于(pre)(max)值在左端点的移动过程中是递增的,所以我们可以考虑递归求解,在递归求解的时候也是通过这个右儿子左儿子的(mx)值和(mx[L])进行比较,如果可以直接计算答案就直接计算,否则继续递归,遇到叶子的话也是返回,因为只有一个节点了可以直接计算

    ​  

    ​  最后就是(pre)的修改,我们只要对每个值用一个(set)随便维护一下下标位置就好啦

    ​  

    ​​  代码大概长这个样子

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<set>
    #define ll long long
    using namespace std;
    const int N=1e5+10,SEG=N*4;
    int pre[N],a[N];
    set<int> rec[N];
    set<int>::iterator it,fr,nxt;
    int n,m;
    ll sum(int l,int r){return 1LL*(l+r)*(r-l+1)/2;}
    namespace Seg{/*{{{*/
    	int ch[SEG][2],mx[SEG];
    	ll ans[SEG];
    	int n,tot;
    	ll calc(int x,int l,int r,int L){
    		int mid=l+r>>1;
    		if (l==r) return l-max(mx[L],mx[x]);
    		if (mx[ch[x][0]]<=mx[L])
    			return 1LL*(l-1-mx[L])*(mid-l+1)+sum(1,mid-l+1)+calc(ch[x][1],mid+1,r,L);
    		else
    			return (ans[x]-ans[ch[x][0]])+calc(ch[x][0],l,mid,L);
    	}
    	void pushup(int x,int l,int r){
    		int mid=l+r>>1;
    		mx[x]=max(mx[ch[x][0]],mx[ch[x][1]]);
    		ans[x]=ans[ch[x][0]];
    		if (mx[ch[x][0]]>=mx[ch[x][1]])
    			ans[x]+=1LL*(mid-mx[ch[x][0]])*(r-mid)+sum(1,r-mid);
    		else
    			ans[x]+=calc(ch[x][1],mid+1,r,ch[x][0]);
    	}
    	void _build(int x,int l,int r){
    		mx[x]=0;
    		if (l==r){
    			mx[x]=pre[l]; ans[x]=l-pre[l];
    			return;
    		}
    		int mid=l+r>>1;
    		ch[x][0]=++tot; _build(ch[x][0],l,mid);
    		ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
    		pushup(x,l,r);
    	}
    	void build(int _n){n=_n; tot=1; _build(1,1,n);}
    	void _update(int x,int d,int lx,int rx){
    		if (lx==rx){
    			mx[x]=pre[lx]; ans[x]=lx-pre[lx];
    			return;
    		}
    		int mid=lx+rx>>1;
    		if (d<=mid) _update(ch[x][0],d,lx,mid);
    		else _update(ch[x][1],d,mid+1,rx);
    		pushup(x,lx,rx);
    	}
    	void update(int d){_update(1,d,1,n);}
    	void debug(int x,int l,int r){
    		if (l==r){printf("%lld ",ans[x]); return;}
    		int mid=l+r>>1;
    		debug(ch[x][0],l,mid);
    		debug(ch[x][1],mid+1,r);
    	}
    	void debug(){debug(1,1,n);}
    }/*}}}*/
    void update(int x,int delta){
    	it=rec[a[x]].find(x);
    	fr=it; nxt=it; --fr; ++nxt;
    	if (nxt!=rec[a[x]].end()){
    		pre[*nxt]=*fr;
    		Seg::update(*nxt);
    	}
    	rec[a[x]].erase(x); 
    	a[x]=delta;
    	rec[a[x]].insert(x);
    
    	it=rec[a[x]].find(x);
    	fr=it; nxt=it; --fr; ++nxt;
    	if (nxt!=rec[a[x]].end()){
    		pre[*nxt]=x;
    		Seg::update(*nxt);
    	}
    	pre[x]=*fr;
    	Seg::update(x);
    }
    void debug(){
    	Seg::debug();
    	printf("
    ");
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	int op,x,to;
    	scanf("%d",&n);
    	for (int i=1;i<=n;++i) rec[i].insert(0);
    	for (int i=1;i<=n;++i){
    		scanf("%d",a+i);
    		pre[i]=*(--rec[a[i]].end());
    		rec[a[i]].insert(i);
    	}
    	Seg::build(n);
    	scanf("%d",&m);
    	for (int i=1;i<=m;++i){
    		scanf("%d",&op);
    		if (op==0) 
    			printf("%lld
    ",Seg::ans[1]);
    		else{
    			scanf("%d%d",&x,&to);
    			update(x,to);
    		}
    		//debug();
    	}
    }
    
  • 相关阅读:
    康威定律和系统设计——《微服务设计》读书笔记
    安全——《微服务设计》读书笔记
    监控——《微服务设计》读书笔记
    测试——《微服务设计》读书笔记
    [转]Linux 系统挂载数据盘
    部署:持续集成(CI)与持续交付(CD)——《微服务设计》读书笔记
    拆分:分解单块系统——《微服务设计》读书笔记
    Unused Method(不再使用的方法)——Dead Code(死亡代码)
    使用Fortify进行代码静态分析(系列文章)
    NEUACM1132: Renew MST Quickly 增量最小生成树
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9721182.html
Copyright © 2011-2022 走看看