BZOJ3578 GTY的人类基因组计划2

版权声明:随意转载,愿意的话提一句作者就好了 https://blog.csdn.net/stone41123/article/details/84061266

Link

Difficulty

算法难度4,思维难度6,代码难度4

Description

一开始给定 n n 个元素,都位于第一个集合中

m m 个集合,编号1~m

q q 次操作,分两种类型:

  1. 将一个元素移动到另一个集合里
  2. 询问编号在 [ l , r ] [l,r] 之间的以前没有询问过的集合的大小之和

1 n , m , q 1 0 5 1\le n,m,q\le 10^5

Solution

首先考虑判断一个集合是否出现过如何判断

一个常用的trick是给每个元素赋一个随机权值,然后集合的权值就是所有元素的权值的异或和

如果两个集合的权值相同则认为它们相同

如果权值范围大的话,那么这种方法是基本不会错的

(错误率大概是有两个权值撞了并且其他元素相同,可以想象一下有多小)

然后就直接map维护这种集合是否出现过,set维护合法的集合标号

时间复杂度 O ( n l o g n ) O(nlogn)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#include<map>
#include<set>
#define LL long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=' ';
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return f==1?x:-x;
}
const int N=1e5+5;
inline double Rand(){
	return (double)rand()/(double)RAND_MAX;
}
set<int> s;
map<LL,int> mp;
int n,m,q;
LL val[N],sum[N];
int cnt[N],p[N];
int main(){
	srand(20020719);
	n=read();m=read();q=read();
	for(int i=1;i<=n;++i)val[i]=(LL)(Rand()*1e9+1)*(LL)(Rand()*1e9+1);
	for(int i=1;i<=n;++i)sum[1]^=val[i],p[i]=1;
	cnt[1]=n;s.insert(1);
	for(int i=1;i<=q;++i){
		char ch[3];
		scanf("%s",ch+1);
		if(ch[1]=='C'){
			int x=read(),y=read();
			if(p[x]==y)continue;
		    s.erase(p[x]);s.erase(y);
			sum[p[x]]^=val[x];
			cnt[p[x]]--;
			if(!mp[sum[p[x]]])s.insert(p[x]);
			sum[y]^=val[x];
			cnt[y]++;
			if(!mp[sum[y]])s.insert(y);
			p[x]=y;
		}
		else{
			int l=read(),r=read();
			LL ans=0;
			for(set<int>::iterator it=s.lower_bound(l);it!=s.end();){
				if((*it)>r)break;
				ans+=cnt[*it];
				mp[sum[*it]]=1;
				set<int>::iterator it2=it;
				++it2;
				s.erase(it);
				it=it2;
			}
			printf("%lld\n",ans);
		}
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/stone41123/article/details/84061266