本题的意思是:给一个序列,有两种操作,第一个操作是修改某一个位置的值,第二个操作是询问整个序列中最长的两个不重合同构子串的长度。(同构的定义如题目所属,是字串中所有出现的数字,在两个子串中的出现次数都相同)
这道题还是想了很久,知道了答案就是相同数字的距离的最大值可是,不知道怎么维护,刚刚开始用线段树,结果太菜写不出来,后来看了大佬的代码,忽然知道了,用set储存每个数字出现的值,然后用multiset 保存每个数的最大距离,不能用set,因为同一距离的数可能有多个,如果用set在单点修改时会出错,最后需要注意在离散化时要把待修改的值一起离散化,然进行离线处理
在修改时,先把当前位置去除,更新去除后这个数的距离最大值,然后再把新的数加入,在进行更新。
代码如下:
///#include<bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<set>
#include<stack>
#include<string>
#include<map>
using namespace std;
typedef long long ll;
const int N=200010;
set<int>ss[N];
int a[N],x[N],c[N],y[N];
vector<int>v;
int getid(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
int n,m;
multiset<int>mmp;
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
v.clear();
mmp.clear();
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
v.push_back(a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&c[i]);
if(c[i]==1)
{
scanf("%d%d",&x[i],&y[i]);
v.push_back(y[i]);
}
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int cnt=v.size();
for(int i=1;i<=cnt;i++)
{
ss[i].clear();
}
for(int i=1;i<=n;i++)
{
a[i]=getid(a[i]);
ss[a[i]].insert(i);
}
for(int i=1;i<=cnt;i++)
{
if(!ss[i].empty())
mmp.insert(*--ss[i].end()-*ss[i].begin());
}
for(int i=1;i<=m;i++)
{
if(c[i]==1)
{
int tmp=a[x[i]];
mmp.erase(mmp.find(*--ss[tmp].end()-*ss[tmp].begin()));
ss[tmp].erase(ss[tmp].find(x[i]));
if(!ss[tmp].empty())
mmp.insert(*--ss[tmp].end()-*ss[tmp].begin());
a[x[i]]=getid(y[i]);
tmp=getid(y[i]);
if(!ss[tmp].empty())
mmp.erase(mmp.find(*--ss[tmp].end()-*ss[tmp].begin()));
ss[tmp].insert(x[i]);
mmp.insert(*--ss[tmp].end()-*ss[tmp].begin());
}
else
{
int res=-1;
if(!mmp.empty())
{
res=*--mmp.end();
}
if(res==0)res=-1;
printf("%d\n",res);
}
}
}
return 0;
}