[ABC265G] 012 Inversion

Problem Statement

You are given a sequence $A=(A_1,\ldots,A_N)$ of length $N$. Each element is $0$, $1$, or $2$.
Process $Q$ queries in order. Each query is of one of the following kinds:

  • 1 L R: print the inversion number of the sequence $(A_L,\ldots,A_R)$.
  • 2 L R S T U: for each $i$ such that $L\leq i \leq R$, if $A_i$ is $0$, replace it with $S$; if $A_i$ is $1$, replace it with $T$; if $A_i$ is $2$, replace it with $U$.
What is the inversion number? The inversion number of a sequence $B = (B_1, \ldots, B_M)$ is the number of pairs of integers $(i, j)$ $(1 \leq i < j \leq M)$ such that $B_i > B_j$.

Constraints

  • $1 \leq N \leq 10^5$
  • $0 \leq A_i \leq 2$
  • $1\leq Q\leq 10^5$
  • In each query, $1\leq L \leq R \leq N$.
  • In each query of the second kind, $0\leq S,T,U \leq 2$.
  • All values in input are integers.

Input

Input is given from Standard Input in the following format:

$N$ $Q$
$A_1$ $A_2$ $\ldots$ $A_N$
$\rm Query_1$
$\rm Query_2$
$\vdots$
$\rm Query_Q$

$\rm Query_i$ denotes the $i$-th query, which is in one of the following formats:

$1$ $L$ $R$
$2$ $L$ $R$ $S$ $T$ $U$

Output

Print the responses to the queries of the first kind in the given order, separated by newlines.


Sample Input 1

5 3
2 0 2 1 0
1 2 5
2 2 4 2 1 0
1 2 5

Sample Output 1

3
4

Initially, $A=(2,0,2,1,0)$.

  • In the $1$-st query, print the inversion number $3$ of $(A_2,A_3,A_4,A_5)=(0,2,1,0)$.
  • The $2$-nd query makes $A=(2,2,0,1,0)$.
  • In the $3$-rd query, print the inversion number $4$ of $(A_2,A_3,A_4,A_5)=(2,0,1,0)$.

Sample Input 2

3 3
0 1 2
1 1 1
2 1 3 0 0 0
1 1 3

Sample Output 2

0
0

区间修改区间询问,首先考虑线段树。

那么我们可以把一对逆序对分成两类,把线段用线段树分成一个个区间后,逆序对有些两个数在一个区间里,有些不在一个区间里。在一个区间里的逆序对,我们可以每次更新的时候都维护逆序对个数。而不再一个区间里的,我们可以在外面再跑一次

那么具体来说。首先不考虑修改,一个区间的逆序对数量需要分开来记录才好记录。记录 \(p_{i,j}\) 为这个区间中形如 \((i,j)\) 的有序数对有多少个。当然我们还要记录一个区间有多少个0,多少个1,多少个2.

线段树上两个区间合并时,节点 \(o\) 的有序数对 \((i,j)\) 的数量为左儿子 \((i,j)\) 的数量和右儿子 \((i,j)\) 的数量加上左区间 \(i\) 的数量和右区间 \(j\) 的数量之积。

区间修改自然还要打tag,记录这个区间中原有的 0 变成那个数字,1变成那个数字,2变成那个数字。这个 tag 也很好合并,更新时逐个数字更新就好了。

线段树的部分解决了,来看询问时的部分。询问时对于跨过区间的逆序对,我们可以记录在这个区间前面有多少个 0,有多少个 1,有多少个2,然后乘起来加上就好。

#include<bits/stdc++.h>
const int N=1e5+5;
typedef long long LL;
struct node{
	int a[3],s,t,u;
	LL b[3][3];
}tr[N<<2];
LL ret;
int n,op,l,r,s,t,u,q,x,a0,a1,a2;
void pushup(int o)
{
	for(int i=0;i<3;i++)
		tr[o].a[i]=tr[o<<1].a[i]+tr[o<<1|1].a[i];
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
			tr[o].b[i][j]=tr[o<<1].b[i][j]+tr[o<<1|1].b[i][j]+1LL*tr[o<<1].a[i]*tr[o<<1|1].a[j];
}
void turn(int o,int s,int t,int u)
{
	int a[3],p[3];
	LL b[3][3];
	memset(b,0,sizeof(b));
	memset(a,0,sizeof(a));
	p[0]=s,p[1]=t,p[2]=u;
	for(int i=0;i<3;i++)
	{
		a[p[i]]+=tr[o].a[i];
		for(int j=0;j<3;j++)
			b[p[i]][p[j]]+=tr[o].b[i][j];
	}
	memcpy(tr[o].a,a,sizeof(a));
	memcpy(tr[o].b,b,sizeof(b));
}
void add(int o,int s,int t,int u)
{
	if(tr[o].s==-1)
		tr[o].s=s,tr[o].t=t,tr[o].u=u;
	else
	{
		int p[3]={s,t,u};
		tr[o].s=p[tr[o].s];
		tr[o].t=p[tr[o].t];
		tr[o].u=p[tr[o].u];
	}
}
void pushdown(int o)
{
	if(tr[o].s!=-1)
	{
		turn(o<<1,tr[o].s,tr[o].t,tr[o].u);
		turn(o<<1|1,tr[o].s,tr[o].t,tr[o].u);
		add(o<<1,tr[o].s,tr[o].t,tr[o].u);
		add(o<<1|1,tr[o].s,tr[o].t,tr[o].u);
		tr[o].s=tr[o].t=tr[o].u=-1;
	}
}
void solve(int o,int l,int r)
{
	if(l>r)
		return;
	if(l==r)
	{
		scanf("%d",&x);
		tr[o].a[x]++;
		return;
	}
	int md=l+r>>1;
	solve(o<<1,l,md);
	solve(o<<1|1,md+1,r);
	pushup(o);
	tr[o].s=tr[o].t=tr[o].u=-1;
}
LL query(int o,int l,int r,int x,int y)
{
	if(x<=l&&r<=y)
	{
		ret+=1LL*a2*tr[o].a[1];
		ret+=1LL*a1*tr[o].a[0];
		ret+=1LL*a2*tr[o].a[0]; 
		a0+=tr[o].a[0];
		a1+=tr[o].a[1];
		a2+=tr[o].a[2];
		return tr[o].b[2][1]+tr[o].b[2][0]+tr[o].b[1][0];
	}
	pushdown(o);
	int md=l+r>>1;
	LL ret=0;
	if(md>=x)
		ret+=query(o<<1,l,md,x,y);
	if(md<y)
		ret+=query(o<<1|1,md+1,r,x,y);
	return ret;
}
void update(int o,int l,int r,int x,int y,int s,int t,int u)
{
	if(x<=l&&r<=y)
	{
		turn(o,s,t,u);
		add(o,s,t,u);
		return;
	}
	pushdown(o);
	int md=l+r>>1;
	if(md>=x)
		update(o<<1,l,md,x,y,s,t,u);
	if(md<y)
		update(o<<1|1,md+1,r,x,y,s,t,u);
	pushup(o);
}
int main() 
{
	scanf("%d%d",&n,&q);
	solve(1,1,n);
	while(q--)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d",&l,&r),ret=a0=a1=a2=0;
			printf("%lld\n",query(1,1,n,l,r)+ret);
		}
		else
		{
			scanf("%d%d%d%d%d",&l,&r,&s,&t,&u);
			update(1,1,n,l,r,s,t,u);
		}
	}
}
posted @ 2022-09-29 22:37  灰鲭鲨  阅读(75)  评论(0编辑  收藏  举报