[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);
}
}
}