COCI 2015/2016 Contest#3 E

Description:

有一个长度为 n n n的序列 A A A,且 A i ∈ [ 1 , K ] A_i \in[1,K] Ai[1,K]
现在有m个操作,有2种操作:
1 x x x v v v表示将位置 A x A_x Ax改为 v v v
2 输出最短的区间长度,满足该区间包含 [ 1 , K ] [1,K] [1,K]的数。
n , m ≤ 50000 , K ≤ 30 n,m\le 50000,K\le 30 n,m50000,K30

Solution:

  • 对于 Θ ( n m ) \Theta(nm) Θ(nm)的做法,是用单调性来维护。
  • 而发现 K K K的范围比较小,那么不难猜到正解的复杂度是 Θ ( m K log ⁡ n ) \Theta(mK\log n) Θ(mKlogn)
  • 因为带修改且实时询问的数据结构,我们很容易会想到用线段树。
  • 但是呢,这个合并不是直接合并的。
  • 对于每种数,我们需要记录左区间的最后一次出现的序列的位置,以及右区间的第一次的出现的序列的位置。
  • 这样,再类似于 Θ ( n m ) \Theta(nm) Θ(nm)的单调性即可合并。

Code:

#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long
templateinline bool chkmax(T &x,T y){return xinline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
templateinline void rd(T&x){
	char c;x=0;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=50002,M=32,INF=0x3f3f3f3f;

int n,m,q;
int A[N];
int cnt[M];

struct p20{
	void solve(){
		while(q--){
			int op,x,v;scanf("%d",&op);
			if(op==1){
				scanf("%d%d",&x,&v);
				A[x]=v;
			}
			else {
				int ans=-1;
				REP(len,m,n){
					REP(l,1,n-len+1){
						int r=l+len-1;
						memset(cnt,0,sizeof cnt);
						REP(i,l,r) cnt[A[i]]++;
						bool flag=1;
						REP(i,1,m) if(!cnt[i]) {flag=0;break;}
						if(flag) {
							ans=len;
							goto loop; 
						}
					}
				}
				loop:;
				printf("%d\n",ans);
			}
		}
	}
}p1;

struct p50{
	
	bool check(){
		REP(i,1,m) if(!cnt[i])return 0;
		return 1;
	}
	
	int work(){
		int ans=INF;
		memset(cnt,0,sizeof cnt);
		int R=0,L=1;
		REP(i,1,n){
			R++;
			cnt[A[R]]++;
			while(L1)cnt[A[L]]--,L++;
			if(check()) chkmin(ans,R-L+1);
		}
		return ans==INF?-1:ans;
	}
	
	void solve(){
		
		while(q--){
			int op,x,v;
			scanf("%d",&op);
			if(op==1){
				scanf("%d%d",&x,&v);
				A[x]=v;
			}
			else printf("%d\n",work());
		}
	}
}p2;

int mark1[M],mark2[M];
struct p100{
	
	struct Tree{
		#define lson L,mid,p<<1
		#define rson mid+1,R,p<<1|1
		#define family tree[p],tree[p<<1],tree[p<<1|1]
		
		struct node{
			int L,R;
			int len,num;
			int mn[M],mx[M];
		}tree[N<<2];
		
		void Up(node &P,node L,node R){
			int sz1=0,sz2=0;
			SREP(i,0,L.num){
				P.mn[sz1++]=L.mn[i];
				mark1[A[L.mn[i]]]=1;
			}
			SREP(i,0,R.num){
				P.mx[sz2++]=R.mx[i];
				mark2[A[R.mx[i]]]=1;
				
				if(mark1[A[R.mn[i]]])continue;
				P.mn[sz1++]=R.mn[i];
			}
			SREP(i,0,L.num){
				if(mark2[A[L.mx[i]]])continue;
				P.mx[sz2++]=L.mx[i];
			}
			
			P.num=sz1;
			chkmin(P.len=L.len,R.len);
			
			memset(mark1,0,sizeof mark1);
			memset(mark2,0,sizeof mark2);
			
			if(sz1>1;
			build(lson),build(rson);
			Up(family); 
		}
		
		void update(int x,int p,int v){
			if(tree[p].L==tree[p].R){
				A[x]=v;
				return;
			}
			int mid=(tree[p].L+tree[p].R)>>1;
			if(x<=mid)update(x,p<<1,v);
			else update(x,p<<1|1,v);
			Up(family);
		}
	}T;
	
	void solve(){
		T.build(1,n,1);
		while(q--){
			int op,x,v;
			rd(op);
			if(op==1) rd(x),rd(v),T.update(x,1,v);
			else printf("%d\n",T.tree[1].len==INF?-1:T.tree[1].len);
		}
	}
}p3;

int main(){
//	freopen("story.in","r",stdin);
//	freopen("story.out","w",stdout);
	rd(n),rd(m),rd(q);
	REP(i,1,n) rd(A[i]);
	
	if(n<=300 and q<=300) p1.solve();
	else if(n<=5000 and q<=5000) p2.solve();
	else p3.solve();
	
	return 0;
}

你可能感兴趣的:(算法,数据结构)