[BZOJ4889][Tjoi2017]不勤勞的圖書管理員-分塊-樹狀陣列
不勤勞的圖書管理員
Description
加里敦大學有個帝國圖書館,小豆是圖書館閱覽室的一個書籍管理員。他的任務是把書排成有序的,所以無序的書讓他產生厭煩,兩本亂序的書會讓小豆產生這兩本書頁數的和的厭煩度。現在有n本被打亂順序的書,在接下來m天中每天都會因為讀者的閱覽導致書籍順序改變位置。因為小豆被要求在接下來的m天中至少要整理一次圖書。小豆想知道,如果他前i天不去整理,第i天他的厭煩度是多少,這樣他好選擇厭煩度最小的那天去整理。
Input
第一行會有兩個數,n,m分別表示有n本書,m天
接下來n行,每行兩個數,ai和vi,分別表示第i本書本來應該放在ai的位置,這本書有vi頁,保證不會有放置同一個位置的書
接下來m行,每行兩個數,xj和yj,表示在第j天的第xj本書會和第yj本書會因為讀者閱讀交換位置
Output
一共m行,每行一個數,第i行表示前i天不去整理,第i天小豆的厭煩度,因為這個數可能很大,所以將結果模10^9 +7後輸出
Sample Input
5 5
1 1
2 2
3 3
4 4
5 5
1 5
1 5
2 4
5 3
1 3
Sample Output
42
0
18
28
48
HINT
對於20%的資料,1 ≤ ai; xj; yj ≤ n ≤ 5000, m ≤ 5000, vi ≤ 10^5
對於100%的資料,1 ≤ ai; xj; yj ≤ n ≤ 50000, m ≤ 50000, vi ≤ 10^5
爆炸OJ日常題面爆炸……
居然不放題面上來……
還好洛谷有題面……
另外調了整整兩個小時啊啊啊啊啊
(╯‵□′)╯︵┻━┻
思路:
首先使用樹狀陣列計算初始答案,可以發現互相形成逆序關係的數對具有貢獻,使用兩個bit,一個像普通的計算逆序對一般維護,另一個插入數字時插入v[i],即可計算初始答案。
對於每一次交換位置,可以發現所有與a[x]和a[y]改變了逆序關係的數字的貢獻會改變,而這些改變僅會出現在[x,y]區間的數與x或y之間。
那麼考慮分塊,對每個塊內部進行排序,對於交換的位置之間的完整塊,在塊內二分,a值小於二分目標的所有值的貢獻的變化相同(就是被二分到的位置切開的兩段的貢獻分別需要在答案中加上和減去)。
此時記個排序後的塊內v值的字首和,對x和y均進行二分,即可做到單塊操作
對於不完整的塊,考慮暴力計算貢獻的變化,並暴力重新排序及計算字首和。複雜度同樣為
直接分成
細節巨多無比,需要仔細檢察程式碼……
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || '9'<ch)ch=getchar();
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
typedef long long ll;
const int M=1e5+9;
const int N=50009;
const ll md=1e9+7;
int n,q;
int st[N],ed[N],id[N],a[N];
ll ans,sum[N],v[N],bit1[M],bit2[M];
inline void cal(ll &x){x%=md;if(x<0)x=(x%md+md)%md;}
inline bool cmp(int x,int y)
{
return a[x]<a[y];
}
inline void add(ll *bit,int p,ll v)
{
for(int i=p;i<M;i+=(i&-i))
bit[i]=((bit[i]+v)%md+md)%md;
}
inline ll query(ll *bit,int p)
{
ll ret=0;
for(int i=p;i;i-=(i&-i))
(ret+=bit[i])%=md;
return ret;
}
int main()
{
n=read();q=read();
int blk=sqrt(n);
for(int i=1;i<=n;i++)
{
a[i]=read();
v[i]=read()%md;
id[i]=i;
if(!st[i/blk])
{
st[i/blk]=i;
if(i-1)
ed[(i-1)/blk]=i-1;
}
}
ed[n/blk]=n;
for(int i=0,e=n/blk;i<=e;i++)
{
sort(id+st[i],id+ed[i]+1,cmp);
sum[st[i]]=v[id[st[i]]];
for(int j=st[i]+1;j<=ed[i];j++)
sum[j]=(sum[j-1]+v[id[j]])%md;
}
ans=0;
for(int i=n;i>=1;i--)
{
ans=(ans+query(bit1,a[i])+query(bit2,a[i])*v[i]%md)%md;
add(bit1,a[i],v[i]);add(bit2,a[i],1);
}
while(q--)
{
int x=read(),y=read();
if(x>y)swap(x,y);
int lb=x/blk,rb=y/blk;
if(lb==rb)
{
for(int i=x;i<=y;i++)
{
if(x<i)
{
if(a[x]<a[i])
cal(ans+=(v[x]+v[i])%md);
else
cal(ans-=(v[x]+v[i])%md);
}
if(i!=x && i<y)
{
if(a[i]<a[y])
cal(ans+=(v[y]+v[i])%md);
else
cal(ans-=(v[i]+v[y])%md);
}
}
swap(a[x],a[y]);
swap(v[x],v[y]);
sort(id+st[lb],id+ed[lb]+1,cmp);
sum[st[lb]]=v[id[st[lb]]];
for(int j=st[lb]+1;j<=ed[lb];j++)
sum[j]=(sum[j-1]+v[id[j]])%md;
cal(ans);
printf("%lld\n",ans);
continue;
}
for(int i=x+1;i<=ed[lb];i++)
if(a[x]<a[i])
cal(ans+=v[x]+v[i]);
else
cal(ans-=v[x]+v[i]);
for(int i=st[rb];i<=y;i++)
if(a[x]<a[i])
cal(ans+=v[x]+v[i]);
else
cal(ans-=v[x]+v[i]);
for(int i=x+1;i<=ed[lb];i++)
if(a[i]<a[y])
cal(ans+=v[y]+v[i]);
else
cal(ans-=v[y]+v[i]);
for(int i=st[rb];i<y;i++)
if(a[i]<a[y])
cal(ans+=v[y]+v[i]);
else
cal(ans-=v[y]+v[i]);
for(int i=lb+1;i<rb;i++)
{
int l=st[i],r=ed[i],val=st[i]-1,mid;
while(l<=r)
{
mid=l+r>>1;
if(a[id[mid]]<a[x])
l=mid+1,val=mid;
else
r=mid-1;
}
cal(ans+=sum[ed[i]]-2*sum[val<st[i]?0:val]%md);
cal(ans+=v[x]*(ed[i]-2*val+st[i]-1)%md);
l=st[i],r=ed[i],val=st[i]-1;
while(l<=r)
{
mid=l+r>>1;
if(a[id[mid]]<a[y])
l=mid+1,val=mid;
else
r=mid-1;
}
cal(ans+=2*sum[val<st[i]?0:val]%md-sum[ed[i]]);
cal(ans+=v[y]*(2*val-ed[i]-st[i]+1)%md);
}
swap(a[x],a[y]);
swap(v[x],v[y]);
sort(id+st[lb],id+ed[lb]+1,cmp);
sort(id+st[rb],id+ed[rb]+1,cmp);
sum[st[lb]]=v[id[st[lb]]];
for(int j=st[lb]+1;j<=ed[lb];j++)
sum[j]=(sum[j-1]+v[id[j]])%md;
sum[st[rb]]=v[id[st[rb]]];
for(int j=st[rb]+1;j<=ed[rb];j++)
sum[j]=(sum[j-1]+v[id[j]])%md;
cal(ans);
printf("%lld\n",ans);
}
return 0;
}