zoukankan      html  css  js  c++  java
  • 【BZOJ4889】[Tjoi2017]不勤劳的图书管理员 分块+树状数组

    【BZOJ4889】[Tjoi2017]不勤劳的图书管理员

    题目描述

    加里敦大学有个帝国图书馆,小豆是图书馆阅览室的一个书籍管理员。他的任务是把书排成有序的,所以无序的书让他产生厌烦,两本乱序的书会让小豆产生这两本书页数的和的厌烦度。现在有n本被打乱顺序的书,在接下来m天中每天都会因为读者的阅览导致书籍顺序改变位置。因为小豆被要求在接下来的m天中至少要整理一次图书。小豆想知道,如果他前i天不去整理,第i天他的厌烦度是多少,这样他好选择厌烦度最小的那天去整理。

    输入输出格式

    输入格式:

    第一行会有两个数,n,m分别表示有n本书,m天

    接下来n行,每行两个数,ai和vi,分别表示第i本书本来应该放在ai的位置,这本书有vi页,保证不会有放置同一个位置的书

    接下来m行,每行两个数,xj和yj,表示在第j天的第xj本书会和第yj本书会因为读者阅读交换位置

    输出格式:

    一共m行,每行一个数,第i行表示前i天不去整理,第i天小豆的厌烦度,因为这个数可能很大,所以将结果模10^9 +7后输出

    输入输出样例

    输入样例#1:
    5 5
    1 1
    2 2
    3 3
    4 4
    5 5
    1 5
    1 5
    2 4
    5 3
    1 3
    输出样例#1:
    42
    0
    18
    28
    48

    说明

    对于20%的数据,1 ≤ ai; xj; yj ≤ n ≤ 5000, m ≤ 5000, vi ≤ 10^5

    对于100%的数据,1 ≤ ai; xj; yj ≤ n ≤ 50000, m ≤ 50000, vi ≤ 10^5

    题解:其实就是让你求一个区间中的带权逆序对数,依然用分块。对于每个块,维护两个树状数组s1,s2。s1代表书的个数,s2代表书的页数的前缀和。修改的时候,只有l,r和(l,r)中的数间的逆序对会改变,两边的不会改变。所以先暴力判断连边的小块,再扫中间的大块。设块的大小为B,假设一个块中有num本书的a值比l小,这些数的页数和为sum,那么ans-=sum-(B-num)*v[l]。r类似。

    结果一交上去TLE了,实测109s多,改了一下块的大小就60s了。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int maxn=50010;
    const ll mod=1000000007;
    ll ans;
    int n,m,B;
    ll s[2][250][maxn],v[maxn];
    int p[maxn];
    int rd()
    {
    	int ret=0;	char gc=getchar();
    	while(gc<'0'||gc>'9')	gc=getchar();
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret;
    }
    void updata(int z,int y,int x,ll val)
    {
    	if(!x)	return ;
    	for(int i=x;i<=n;i+=i&-i)	s[z][y][i]=(s[z][y][i]+val+mod)%mod;
    }
    ll query(int z,int y,int x)
    {
    	if(x<0)	return 0;
    	ll ret=0;
    	for(int i=x;i;i-=i&-i)	ret=(ret+s[z][y][i])%mod;
    	return ret;
    }
    void calc(int a,int b,int c)
    {
    	if(p[a]<p[c])	ans=(ans+v[a]+v[c])%mod;
    	else	ans=(ans-v[a]-v[c]+mod+mod)%mod;
    	if(p[b]<p[c])	ans=(ans-v[b]-v[c]+mod+mod)%mod;
    	else	ans=(ans+v[b]+v[c])%mod;
    }
    int main()
    {
    	int i,j,a,b,c,d;
    	n=rd(),m=rd(),B=int(sqrt(n*17));
    	for(i=0;i<n;i++)	p[i]=rd(),v[i]=rd(),updata(0,i/B,p[i],v[i]),updata(1,i/B,p[i],1);
    	for(i=n-1;i>=0;i--)
    	{
    		ans=(ans+query(0,n/B+1,p[i])+query(1,n/B+1,p[i])*v[i])%mod;
    		updata(0,n/B+1,p[i],v[i]),updata(1,n/B+1,p[i],1);
    	}
    	for(i=1;i<=m;i++)
    	{
    		a=rd()-1,b=rd()-1;
    		if(a==b)
    		{
    			printf("%lld
    ",ans);
    			continue;
    		}
    		if(a>b)	swap(a,b);
    		c=a/B,d=b/B;
    		if(p[a]<p[b])	ans=(ans+v[a]+v[b])%mod;
    		else	ans=(ans-v[a]-v[b]+mod+mod)%mod;
    		if(c==d)
    		{
    			for(j=a+1;j<b;j++)	calc(a,b,j);
    			swap(p[a],p[b]),swap(v[a],v[b]);
    			printf("%lld
    ",ans);
    			continue;
    		}
    		for(j=a+1;j<c*B+B;j++)	calc(a,b,j);
    		for(j=d*B;j<b;j++)	calc(a,b,j);
    		for(j=c+1;j<d;j++)
    		{
    			ans=(ans-2*query(0,j,p[a])+query(0,j,n)-(2*query(1,j,p[a])-B+mod)*v[a]%mod+mod)%mod;
    			ans=(ans+2*query(0,j,p[b])-query(0,j,n)+(2*query(1,j,p[b])-B+mod)*v[b]%mod+mod)%mod;
    		}
    		updata(0,c,p[a],-v[a]),updata(0,d,p[b],-v[b]),updata(1,c,p[a],-1),updata(1,d,p[b],-1);
    		swap(p[a],p[b]),swap(v[a],v[b]);
    		updata(0,c,p[a],v[a]),updata(0,d,p[b],v[b]),updata(1,c,p[a],1),updata(1,d,p[b],1);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    //5 5 1 1 2 2 3 3 4 4 5 5 1 5 1 5 2 4 5 3 1 3
  • 相关阅读:
    LeetCode15 3Sum
    LeetCode10 Regular Expression Matching
    LeetCode20 Valid Parentheses
    LeetCode21 Merge Two Sorted Lists
    LeetCode13 Roman to Integer
    LeetCode12 Integer to Roman
    LeetCode11 Container With Most Water
    LeetCode19 Remove Nth Node From End of List
    LeetCode14 Longest Common Prefix
    LeetCode9 Palindrome Number
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7128300.html
Copyright © 2011-2022 走看看