题目描述
a[1][i]<=1e4,n,m<=1e5
解题思路
一眼似乎不可做。随便搞个数据打个表找找规律,发现除了第一行,第i行和第i+2行是一样的。
考虑一种权值为一种颜色。
那么问题变成了询问一个点的前面有多少个和他一样的点记为cnt0,或者询问一个点之前,次数大于等于cnt0的其他颜色的个数cnt1。
考虑分块。
考虑维护前i个块内的每个颜色x的cnt0[x]和每个次数y的cnt1[y]。那么一个元素要插入O(根号)个块里。
一开始,我们可以先把cnt0[]用桶a搞出来。然后用另一个桶b,其中b[x]表示cnt0=x的颜色个数。然后求个后缀和,就把cnt1[]给做出来了。
考虑修改怎么做,发现每次修改某个cnt0-1,某个cnt0+1,观察到前缀和数组只会改变两位,那么修改就是O(根号)的。
考虑求答案。如果求x的cnt0,我们只需要把最大的前缀块拿出来,然后用他的cnt0[x]再加上不在块内的x的个数。
如果求cnt1,我们先求出cnt0,然后答案是,最大的前缀块的cnt1[cnt0],再加上,算上块外的数之后出现次数从小于cnt0变成大于等于cnt0的颜色个数.
时间复杂度O(n sqrt(n))
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=1e5+5,M=4e5+5,mo=1e9+7,xs=1331,B=320;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
struct blk
{
int en;
int a[10005],b[10005];
void predo()
{
int i;
fo(i,1,10000) if (a[i]) b[a[i]]++;
fd(i,10000,0) b[i]+=b[i+1];
}
}bl[B];
int n,a[N],x,y,ka,siz,i,j,cnt,l,m,z,cnt0,cnt1;
int main()
{
freopen("t2.in","r",stdin);
freopen("t2.out","w",stdout);
scanf("%d",&n);
fo(i,1,n) a[i]=read();
siz=trunc(sqrt(n));
cnt=n/siz+(n%siz>0);
fo(i,1,cnt) bl[i].en=min(n,siz*i);
fo(i,1,n)
fo(j,1,cnt) if (i<=bl[j].en)
bl[j].a[a[i]]++;
fo(j,1,cnt) bl[j].predo();
m=read();
fo(l,1,m)
{
ka=read();x=read();y=read();
if (ka==1)
{
fo(i,1,cnt)
if (y<=bl[i].en)
{
bl[i].b[bl[i].a[a[y]]--]--;
bl[i].b[++bl[i].a[x]]++;
}
a[y]=x;
}
if (ka==2)
{
if (x==1) printf("%d\n",a[y]);
else
{
fo(z,1,cnt) if (bl[z].en>y) break;
z--;
cnt0=bl[z].a[a[y]];
fo(i,bl[z].en+1,y) if (a[i]==a[y]) cnt0++;
if (x%2==0)
{
printf("%d\n",cnt0);
}else
{
cnt1=bl[z].b[cnt0];
fo(i,bl[z].en+1,y) if ((++bl[z].a[a[i]])==cnt0) cnt1++;
printf("%d\n",cnt1);
fo(i,bl[z].en+1,y) --bl[z].a[a[i]];
}
}
}
}
m=0;
}