版权声明:随意转载,愿意的话提一句作者就好了 https://blog.csdn.net/stone41123/article/details/84061266
Link
Difficulty
算法难度4,思维难度6,代码难度4
Description
一开始给定 个元素,都位于第一个集合中
有 个集合,编号1~m
次操作,分两种类型:
- 将一个元素移动到另一个集合里
- 询问编号在 之间的以前没有询问过的集合的大小之和
Solution
首先考虑判断一个集合是否出现过如何判断
一个常用的trick是给每个元素赋一个随机权值,然后集合的权值就是所有元素的权值的异或和
如果两个集合的权值相同则认为它们相同
如果权值范围大的话,那么这种方法是基本不会错的
(错误率大概是有两个权值撞了并且其他元素相同,可以想象一下有多小)
然后就直接map维护这种集合是否出现过,set维护合法的集合标号
时间复杂度
#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;
}