一、题目
二、解法
很类似的题——排队
其实这道题的本质就是带修的逆序对,可以用树套树实现。
还是把交换操作理解为两次修改,每次减去原来的贡献在加上新的贡献,这里比一般逆序对要多维护一些值,要维护个数和
v
v
v值和,计算方式就是 个数
×
\times
×当前
v
+
v
v+v
v+v之和。
这道题的难点并不是上面的解法,而是卡常,我写的树套树只得了签到分qwq。
比如说我们交换
x
,
y
x,y
x,y,其实只有红色的部分需要考虑新的贡献,其他的部分由于相对位置不变就不用了算了,我们可以特判
a
[
x
]
,
a
[
y
]
a[x],a[y]
a[x],a[y]的情况,然后把答案修改 -比a[x]小的数+比a[x]大的数-比a[y]大的数+比a[y]小的数
还有一个空间上的优化,就是写内存池,回收废弃的空间,详细见代码吧。
#include <cstdio>
#include <iostream>
using namespace std;
#define LL long long
const int MAXN = 50005;
const int MAXM = 200*MAXN;
const int MOD = 1e9+7;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,cnt,a1,b1,a2,b2,a[MAXN],v[MAXN],rt[MAXN*4];
LL ans,ans1,ans2;
struct node
{
int ls,rs,num;
LL sum;
} tr[MAXM];
struct Memory_Pool
{
int top,p[MAXM];
Memory_Pool()
{
top=MAXM-1;
for(int i=1; i<=top; i++)
p[i]=i;
}
int New()
{
return p[top--];
}
void Rec(int x)
{
p[++top]=x;
}
} M;
void up(int x)
{
tr[x].num=tr[tr[x].ls].num+tr[tr[x].rs].num;
tr[x].sum=tr[tr[x].ls].sum+tr[tr[x].rs].sum;
}
void ins(int &x,int l,int r,int id,int val)
{
if(!x)
{
x=M.New();
tr[x].ls=tr[x].rs=tr[x].num=tr[x].sum=0;
}
if(l==r)
{
tr[x].num++;
tr[x].sum+=val;
return ;
}
int mid=(l+r)>>1;
if(mid>=id)
ins(tr[x].ls,l,mid,id,val);
else
ins(tr[x].rs,mid+1,r,id,val);
up(x);
}
void Modify(int i,int l,int r,int id)
{
ins(rt[i],1,n,a[id],v[id]);
if(l==r) return ;
int mid=(l+r)>>1;
if(mid>=id)
Modify(i<<1,l,mid,id);
else
Modify(i<<1|1,mid+1,r,id);
}
void del(int &x,int l,int r,int id,int val)
{
if(!x) return ;
if(l==r)
{
tr[x].num--;
tr[x].sum-=val;
if(tr[x].num==0)
{
M.Rec(x);
x=0;
}
return ;
}
int mid=(l+r)>>1;
if(mid>=id)
del(tr[x].ls,l,mid,id,val);
else
del(tr[x].rs,mid+1,r,id,val);
if(!tr[x].ls && !tr[x].rs)
{
M.Rec(x);
x=0;
return ;
}
up(x);
}
void Remove(int i,int l,int r,int id)
{
del(rt[i],1,n,a[id],v[id]);
if(l==r) return ;
int mid=(l+r)>>1;
if(mid>=id)
Remove(i<<1,l,mid,id);
else
Remove(i<<1|1,mid+1,r,id);
}
void query(int x,int l,int r,int L,int R)
{
if(L>R || !x || l>R || L>r) return ;
if(L<=l && r<=R)
{
ans1+=tr[x].num;
ans2+=tr[x].sum;
return ;
}
int mid=(l+r)>>1;
query(tr[x].ls,l,mid,L,R);
query(tr[x].rs,mid+1,r,L,R);
}
void Query(int i,int l,int r,int L,int R,int id)
{
if(L>R || l>R || L>r) return ;
if(L<=l && r<=R)
{
ans1=ans2=0;
query(rt[i],1,n,a1,b1);
ans-=ans1*v[id]+ans2;
ans1=ans2=0;
query(rt[i],1,n,a2,b2);
ans+=ans1*v[id]+ans2;
return ;
}
int mid=(l+r)>>1;
Query(i<<1,l,mid,L,R,id);
Query(i<<1|1,mid+1,r,L,R,id);
}
void init(int x1,int y1,int x2,int y2)
{
a1=x1;
b1=y1;
a2=x2;
b2=y2;
}
signed main()
{
n=read();
m=read();
for(int i=1; i<=n; i++)
{
a[i]=read();
v[i]=read();
Modify(1,1,n,i);
}
for(int i=1; i<=n; i++)
{
init(1,0,a[i]+1,n);
Query(1,1,n,1,i-1,i);
}
for(int i=1; i<=m; i++)
{
int x=read(),y=read(),t1,t2;
if(x==y) goto In;
if(x>y) swap(x,y);
t1=a[x],t2=v[x];
if(a[x]<a[y]) ans+=v[x]+v[y];
else ans-=v[x]+v[y];
init(1,a[x]-1,a[x]+1,n);
Query(1,1,n,x+1,y-1,x);
init(a[y]+1,n,1,a[y]-1);
Query(1,1,n,x+1,y-1,y);
Remove(1,1,n,x);
a[x]=a[y];
v[x]=v[y];
Modify(1,1,n,x);
Remove(1,1,n,y);
a[y]=t1;
v[y]=t2;
Modify(1,1,n,y);
In:
printf("%lld\n",(ans%MOD+MOD)%MOD);
}
}