【问题描述】
给出一个包含正整数的数组P(p1,…,pn),并假设数组P经过排序后变成数组Q(q1,…,qn)。定义合法替换集合,如果整数对(X,Y)属于合法交换集合,则表示你可以交换数组p中位置X和位置Y的元素。现在有Q个操作(询问),询问分为以下四种类型:
1.交换位置A和位置B的元素
2.将整数对(A,B)加入到合法交换集合
3.询问是否能通过合法交换集合中的操作,将数组P进行排序。(注意:过程中可以以任何顺序执行交换操作,并且每个交换操作可以执行任意多次)
4.定义位置对(A,B)是相连的当且仅当位置A的元素可以仅通过合法交换集合中的操作移动到位置B。
定义所有和位置A相连的位置组成的集合为A的群集。如果对于每一个在A的群集中的位置j,都能通过一系列合法交换集合中的操作使得pj=qj,则称A的群集是良好的。 你需要回答,有多少对不同的位置对(A,B)满足:
a)位置A和位置B是不相连的
b)A的群集和B的群集都不是良好的
c)如果我们将位置对(A,B)加入到合法交换集合中,A的群集将变成良好的。
注意:在计算时,位置对(A,B)和位置对(B,A)被视为不同的位置对。
【输入格式】
第一行两个正整数,N和Q(1<=N, Q<=1,000,000)
第二行包含N个正整数p1,…,pn(1<=p1,…,pn<=1,000,000)
接下来Q行,每行表示一个操作:
每行第一个数字表示操作的种类,(1,2,3或4)
如果操作种类是1或2,后面紧接着两个正整数A,B(1<=A,B<=N)表示位置对(A,B)
【输出格式】
对于每一个询问(种类为3、4的操作),输出一行作为询问的答案。
对于种类为3的询问的答案输出“YES”或“NO”(不包含双引号)。“YES”表示能通过合法交换集合的操作将数组P排序,“NO”则表示不能。
对于种类为4的询问输出,输出一个非负整数表示对应的答案。
【输入样例】
【样例1】
3 5
1 3 2
4
3
2 2 3
4
3
【样例2】
5 5
4 2 1 4 4
3
4
1 1 3
3
4
【样例3】
4 10
2 1 4 3
3
4
1 1 2
3
4
2 2 3
2 1 2
4
2 3 4
3
【输出样例】
【样例1】
1
NO
0
YES
【样例2】
NO
1
YES
0
【样例3】
NO
2
NO
1
3
YES
【样例解释】
【样例解释1】
第一个询问答案是1,因为只有位置对(2,3)符合所有的条件,第二个询问答案是NO,因为交换集合为空,数字2和3没有办法交换到正确位置,在第三次操作后,我们把位置对(2,3)加入到交换集合中,第四次操作(询问)答案为0因为2和3已经相连,第五次操作(询问)答案是YES,因为可以通过位置对(2,3)把数组排好序。
【数据范围】
50%的数据满足N,Q<=1000。
【来源】
这道题一看就知道是并查集,但具体怎么做就难了。
要定义一个哈希函数,来不重复的代表每个数的权值。
然后定义2个数组。
q(i)表示P中i这个并查集所代表的位置的元素的权值和。
p(i)表示Q中i这个并查集所代表的位置的元素的权值和。
然后用map存每个p(i)-q(i)的值。
当我们要找互相配合起来和谐的并查集时就直接找2个差值加起来为0的就可以了,在中间边变化边记录就好。
而要全部和谐就得所有差值都为0,直接看map中为0的个数是不是n个就可以了。
其他就是并查集了。
具体代码如下:
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=1000005;
const ll mod=2000005ll;
ll q[maxn],p[maxn],a[maxn];
map<ll,int>mp;
int n,m,b[maxn],pa[maxn],c[maxn];
int num=0,size[maxn];
int find(int x){return pa[x]==x?x:pa[x]=find(pa[x]);}
void un(int x,int y)
{
int px=find(x),py=find(y);
if(px==py) return;
mp[p[px]-q[px]]-=size[px];
if(q[px]-p[px]!=0) num-=size[px]*mp[q[px]-p[px]];
mp[p[py]-q[py]]-=size[py];
if(q[py]-p[py]!=0)num-=size[py]*mp[q[py]-p[py]];
q[px]+=q[py];
p[px]+=p[py];
size[px]+=size[py];
if(q[px]-p[px]!=0) num+=size[px]*mp[q[px]-p[px]];
mp[p[px]-q[px]]+=size[px];
pa[py]=px;
}
int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
n=read();m=read();
a[1]=17;
for(int i=2;i<=1000000;i++)
{
a[i]=(a[i-1]*17)%mod;
}
int x,id,y,px,py;
for(int i=1;i<=n;i++)
{
c[i]=b[i]=read();
q[i]=a[b[i]];
pa[i]=i;
size[i]=1;
}
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
{
p[i]=a[b[i]];
if(q[i]-p[i]!=0)num+=mp[q[i]-p[i]];
mp[p[i]-q[i]]++;
}
while(m--)
{
id=read();
if(id==1)
{
x=read();y=read();
px=find(x),py=find(y);
if(px==py)
{
swap(c[x],c[y]);
continue;
}
mp[p[px]-q[px]]-=size[px];
if(q[px]-p[px]!=0) num-=size[px]*mp[q[px]-p[px]];
mp[p[py]-q[py]]-=size[py];
if(q[py]-p[py]!=0)num-=size[py]*mp[q[py]-p[py]];
q[px]+=a[c[y]]-a[c[x]];
q[py]+=a[c[x]]-a[c[y]];
mp[p[px]-q[px]]+=size[px];
if(q[px]-p[px]!=0)num+=size[px]*mp[q[px]-p[px]];
mp[p[py]-q[py]]+=size[py];
if(q[py]-p[py]!=0)num+=size[py]*mp[q[py]-p[py]];
swap(c[x],c[y]);
}
if(id==2)
{
x=read();y=read();
un(x,y);
}
if(id==3)
{
if(mp[0]==n) printf("YES\n");
else printf("NO\n");
}
if(id==4)
{
printf("%d\n",num);
}
}
return 0;
}