[COCI 2015/2016 #3] NEKAMELEONI 题解

显然的线段树题目,思考如何利用 \(k\) 较小这个条件,如果 \(k\) 更小一点,比如 \(k=3\) 那这个题就可以 \(2^{k \times 2}\) 的处理 push_up ,具体就是在左边和右边各开一个数组记一下若从左往右或从右往左所经过的状态为 \(st\) 时的最小长度。实现比较简单。

而我们发现很多状态是赘余的,因为有些状态根本走不到,所以可以进行优化,比如可以记一下从左往右第 \(i\) 个与之前不同的数与之前所有的数构成的集合和这个数的下标,下标主要是计算答案的时候比较方便一点,这里写一下 pushup 的转移方法,从左往右的答案,先将左边的值复制下来,需要对前面的数打一个标记,再枚举后面的数,若这个数没打过标记,那么这个会加入贡献。从右往左同理。

最后计算答案时类似一个双指针的写法,将左边的区间的从右往左的答案和右边区间从左往右的答案配对即可。时间复杂度 \(O(kn\log n)\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,inf=1e9,K=51;
int val[N],k,n,m;
bool mark[K];
long long C;
void tomin(int &x,int y) {
	if(x>y)x=y;
}
struct node {
	int l[K],r[K],ans,len;
	long long lst[K],rst[K];
	node(int x=0) {
		memset(l,0,sizeof l);
		memset(r,0,sizeof r);
		memset(lst,0,sizeof lst);
		memset(rst,0,sizeof rst);
		len=1;
		l[0]=r[0]=x;
		lst[0]=rst[0]=(1ll<<val[x]);
		ans=(1<<val[x])==C?1:inf;
	}
	void operator =(const int &x){
		//node res;
		len=1;
		l[0]=r[0]=x;
		lst[0]=rst[0]=(1ll<<val[x]);
		//cout<<(1<<a) <<" "<<res.lst[0]<<endl;
		ans=(1<<val[x])==C?1:inf;
	} 
	//l and lst 分别表示左边往右数,第 i 个出现的数字的下标和其包含的数字集合
	//r and rst 同理
	//ans 为区间答案
	//len 为区间内的数字种类
};
node operator + (const node &a,const node &b) {
	node res;
	res.ans=min(a.ans,b.ans );
	res.len=max(a.len ,b.len );

	memset(mark,0,sizeof mark);
	for(int i=0; i<a.len ; i++)
		res.l[i]=a.l[i],res.lst [i]=a.lst[i],mark[val[a.l[i]]]=1;

	for(int i=0,j=a.len ; i<b.len ; i++) {
		if(!mark[val[b.l[i]]]) {
			res.l[j]=b.l[i];
			res.lst[j]=(res.lst[j-1]|1ll<<val[b.l[i]]);
			j++;res.len =j;
		}
		
	}
	memset(mark,0,sizeof mark);
	for(int i=0; i<b.len ; i++)
		res.r[i]=b.r[i],res.rst [i]=b.rst[i],mark[val[res.r[i]]]=1;
	for(int i=0,j=b.len; i<a.len ; i++) {
		if(!mark[val[a.r[i]]]) {
			res.r[j]=a.r[i];
			res.rst[j]=(res.rst[j-1]|1ll<<val[a.r[i]]);
			j++;
			//cout<<val[a.r[i]]<<endl; 
			res.len =j;
		}
		
	}
//	cout<<res.len <<endl; 
//	if(res.len >k)exit(0);
	if(res.len ==k) {
		for(int i=0,j=b.len-1; i<a.len; i++) {
			while(j>0 && (a.rst[i] | b.lst[j-1])==C)
				j--;
			//cout<<i<<" "<<j<<endl;
			if((a.rst[i] | b.lst [j] )==C )tomin(res.ans, b.l[j]-a.r[i]+1);
			//else break;
		}
	}
	return res;
}
struct SEG {
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
	node c[N<<2];
	void pushup(int p) {
		c[p]=c[ls]+c[rs];
	}
	void build(int p,int l,int r) {
		if(l==r){c[p]=l;
		//	cout<<l<<" "<<r<<" "<<c[p].lst [0]<<" "<<c[p].len<<endl;
			return ; 
		}
		build(ls,l,mid),build(rs,mid+1,r);
		pushup(p);
		//cout<<l<<" "<<r<<" "<<c[p].l[0] <<" "<<c[p].r[0] <<" "<<c[p].l[1]<<" "<<c[p].r[1]<<endl; 
	}
	void change(int p,int l,int r,int x,int w) {
		if(l==r)
			return c[p]=l,void ();
		if(x<=mid)change(ls,l,mid,x,w);
		else change(rs,mid+1,r,x,w);
		pushup(p);
	}
} seg;
signed main() {
	scanf("%d%d%d",&n,&k,&m);
	C= (1ll<<k)-1;
	for(int i=1; i<=n; i++)
		scanf("%d",val+i),val[i]--;
	seg.build(1,1,n);
	for(int i=1,op,x,y; i<=m; i++) {
		scanf("%d",&op);
		if(op==2) {
			printf("%d\n",seg.c[1].ans==inf?-1:seg.c[1].ans);
		} else {
			scanf("%d%d",&x,&y);y--;
			val[x]=y;
			seg.change(1,1,n,x,y);
		}
	}
	return 0;
}
posted @ 2025-07-26 10:40  hnczy  阅读(29)  评论(0)    收藏  举报