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
  • 相关阅读:
    ASP.NET中常用的附件上传下载
    C#中导出Excel的常用方式
    ASP.NET中AjaxPro.dll的简单应用
    在ASP.NET中使用FusionCharts图表
    ASP.NET中使用MagicAjax.dll
    FusionCharts图表导出
    C#中经常注入的一些Javascript代码
    CodeSmith3.2(.net2.0)教程
    您未必知道的Css技巧
    Web Service简介
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7128300.html
Copyright © 2011-2022 走看看