zoukankan      html  css  js  c++  java
  • 题解【[TJOI2017]不勤劳的图书管理员】

    [ exttt{Description} ]

    给两个长度为 (n) 的数列 (A)(B)

    (A) 的逆序对(满足 (x<y)(A_x>A_y) 的数对 ((x,y)) )对答案的贡献为 (B_x+B_y)

    通俗地说,答案就是 (sumlimits_{x < y & A_x > A_y}) (B_x+B_y)

    一共 (m) 次交换,每次交换 (A_x)(A_y)(B_x)(B_y)

    每次交换后,你都要给出答案。

    [ exttt{Solution} ]

    • CF785E 升级版,多加了 (B) 这一维度。

    • 首先,对于初始序列的答案,显然可以用两个 ( ext{BIT}) 来做,一个 ( ext{BIT}) 维护值域内数的数量,一个 ( ext{BIT}) 维护值域内数的 (B) 值和 ,像 ( ext{BIT}) 求逆序对一样地,从后往前扫,考虑当前位置 (x) 上的数 (A_x) ,与 (x+1)(n) 之间的 (A_y) 产生的逆序对对答案的贡献,记 (C_1)(A) 值在 ([1,A_x-1]) 内数的个数,记 (C_2)(A) 值在 ([1,A_x-1]) 内数的 (B) 值和,则对答案的贡献有 (B_x imes C_1+C_2) ,求完整个序列的答案的复杂度是 (mathcal{O(n log n)}) 的。

    • 考虑交换怎么搞,对于任意两个位置 (x,y) ,我们考虑计算出交换 (x,y) 对答案造成的影响。

    • 首先,交换 (x,y) 显然不会影响到 ([1,x-1])([y+1,n]) 中的数与 (A_x)(A_y) 产生的逆序对,因为相对位置是不变的。再者,对于 (A_x)(A_y) 是否会形成逆序对,可以直接讨论一下 (A_x)(A_y) 的大小关系。

    • 于是我们只要考虑 ([x+1,y-1]) 内的数与 (A_x)(A_y) 的逆序对关系,我们可以视交换 (x,y) 为:在 (x) 处除去一个 (A_x) ,在 (x) 处增上一个 (A_y) ,在 (y) 处除去一个 (A_y) ,在 (y) 处增上一个 (A_x)

    • 那么答案的变化应该为:

      减去 " (B_x imes) 区间 ([x+1,y-1])(A) 值在 ([1,A_x-1]) 的数的个数 (+) 区间 ([x+1,y-1])(A) 值在 ([1,A_x-1]) 的数的 (B) 值和 " 。

      加上 " (B_y imes) 区间 ([x+1,y-1])(A) 值在 ([1,A_y-1]) 的数的个数 (+) 区间 ([x+1,y-1])(A) 值在 ([1,A_y-1]) 的数的 (B) 值和 " 。

      减去 " (B_y imes) 区间 ([x+1,y-1])(A) 值在 ([A_y+1,n]) 的数的个数 (+) 区间 ([x+1,y-1])(A) 值在 ([A_y+1,n]) 的数的 (B) 值和 " 。

      加上 " (B_x imes) 区间 ([x+1,y-1])(A) 值在 ([A_x+1,n]) 的数的个数 (+) 区间 ([x+1,y-1])(A) 值在([A_x+1,n]) 的数的 (B) 值和 " 。

    • 我们发现更新答案需要查询 " 区间内的给定值域内的数的信息 " ,并且带修,可以用树套树来做(( ext{BIT}) 套权值线段树,线段树套权值线段树都可以)。

    • (mathcal{O(n log^2 n)})评测记录

    [ exttt{Code} ]

    #include<cstdio>
    #include<algorithm>
    
    #define RI register int
    
    using namespace std;
    
    namespace IO
    {
        static char buf[1<<20],*fs,*ft;
        inline char gc()
        {
            if(fs==ft)
            {
    			ft=(fs=buf)+fread(buf,1,1<<20,stdin);
    			if(fs==ft)return EOF;
            }
            return *fs++;
        }
        #define gc() getchar()
    	inline int read()
    	{
    		int x=0,f=1;char s=gc();
    		while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();}
    		while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
    		return x*f;
    	}
    }using IO::read;
    
    const int N=50100,MLOGNLOGN=20000000;
    
    const int Mod=1e9+7;
    
    void ckadd(long long &x,long long val)
    {
    	x=((x+val)%Mod+Mod)%Mod;
    }
    
    int n,m;
    
    long long a[N],b[N];
    
    long long BIT[N][2]; // 0 个数 : 1 和
    
    void BIT_add(int x,int k,int val)
    {
    	for(;x<=n;x+=x&-x)ckadd(BIT[x][k],val);
    }
    
    long long BIT_ask(int x,int k)
    {
    	long long ans=0;
    	for(;x;x-=x&-x)ckadd(ans,BIT[x][k]);
    	return ans;
    }
    
    long long ans; 
    
    int tot,root[N];
    struct SegmentTree{
    	int lc,rc;
    	long long cnt;
    	long long sum;
    }t[MLOGNLOGN];
    
    int New()
    {
    	tot++;
    	t[tot].lc=t[tot].rc=t[tot].cnt=0;
    	return tot;
    }
    
    void insert(int &p,int l,int r,int delta,int cnt,int sum)
    {
    	if(!p)p=New();
    	ckadd(t[p].cnt,cnt);
    	ckadd(t[p].sum,sum);
    	if(l==r)return;
    	int mid=(l+r)/2;
    	if(delta<=mid)
    		insert(t[p].lc,l,mid,delta,cnt,sum);
    	else
    		insert(t[p].rc,mid+1,r,delta,cnt,sum);
    }
    
    void add(int pos,int delta,int cnt,int sum)
    {
    	for(;pos<=n;pos+=pos&-pos)
    		insert(root[pos],1,n,delta,cnt,sum);
    }
    
    int lenA,addt[N];
    int lenS,subt[N];
    
    long long ask(int L,int R,int k,int s,int e) // 区间 [l,r] 类型 k 值域 [s,e] 
    {
    	if(e==0||s>e)
    		return 0;
    
    	long long res=0;
    	int l,r;
    
    	lenA=0;
    	for(RI i=R;i;i-=i&-i)addt[++lenA]=root[i];
    	lenS=0;
    	for(RI i=L-1;i;i-=i&-i)subt[++lenS]=root[i];
    
    	l=1,r=n;
    	while(true)
    	{
    		if(l==r)
    		{
    			for(RI i=1;i<=lenA;i++)ckadd(res,k?t[addt[i]].sum:t[addt[i]].cnt);
    			for(RI i=1;i<=lenS;i++)ckadd(res,k?-t[subt[i]].sum:-t[subt[i]].cnt);
    			break;
    		}
    		int mid=(l+r)/2;
    		if(e<=mid)
    		{
    			for(RI i=1;i<=lenA;i++)addt[i]=t[addt[i]].lc;
    			for(RI i=1;i<=lenS;i++)subt[i]=t[subt[i]].lc;
    			r=mid;
    		}
    		else
    		{
    			for(RI i=1;i<=lenA;i++)ckadd(res,k?t[t[addt[i]].lc].sum:t[t[addt[i]].lc].cnt);
    			for(RI i=1;i<=lenS;i++)ckadd(res,k?-t[t[subt[i]].lc].sum:-t[t[subt[i]].lc].cnt);
    			for(RI i=1;i<=lenA;i++)addt[i]=t[addt[i]].rc;
    			for(RI i=1;i<=lenS;i++)subt[i]=t[subt[i]].rc;
    			l=mid+1;
    		}
    	}
    
    	if(s==1)
    		return res;
    
    	lenA=0;
    	for(RI i=R;i;i-=i&-i)addt[++lenA]=root[i];
    	lenS=0;
    	for(RI i=L-1;i;i-=i&-i)subt[++lenS]=root[i];
    
    	l=1,r=n;
    	while(true)
    	{
    		if(l==r)
    		{
    			for(RI i=1;i<=lenA;i++)ckadd(res,k?-t[addt[i]].sum:-t[addt[i]].cnt);
    			for(RI i=1;i<=lenS;i++)ckadd(res,k?t[subt[i]].sum:t[subt[i]].cnt);
    			break;
    		}
    		int mid=(l+r)/2;
    		if(s-1<=mid)
    		{
    			for(RI i=1;i<=lenA;i++)addt[i]=t[addt[i]].lc;
    			for(RI i=1;i<=lenS;i++)subt[i]=t[subt[i]].lc;
    			r=mid;
    		}
    		else
    		{
    			for(RI i=1;i<=lenA;i++)ckadd(res,k?-t[t[addt[i]].lc].sum:-t[t[addt[i]].lc].cnt);
    			for(RI i=1;i<=lenS;i++)ckadd(res,k?t[t[subt[i]].lc].sum:t[t[subt[i]].lc].cnt);
    			for(RI i=1;i<=lenA;i++)addt[i]=t[addt[i]].rc;
    			for(RI i=1;i<=lenS;i++)subt[i]=t[subt[i]].rc;
    			l=mid+1;
    		}
    	}
    
    	return res;
    }
    
    void turn(int x,int y)
    {
    	if(x>y)
    		swap(x,y);
    
    	if(x+1<=y-1)
    	{
    		ckadd(ans,-b[x]*ask(x+1,y-1,0,1,a[x]-1)-ask(x+1,y-1,1,1,a[x]-1));
    		ckadd(ans,-b[y]*ask(x+1,y-1,0,a[y]+1,n)-ask(x+1,y-1,1,a[y]+1,n));
    		ckadd(ans,b[y]*ask(x+1,y-1,0,1,a[y]-1)+ask(x+1,y-1,1,1,a[y]-1));
    		ckadd(ans,b[x]*ask(x+1,y-1,0,a[x]+1,n)+ask(x+1,y-1,1,a[x]+1,n));
    	}
    
    	if(a[x]>a[y])
    		ckadd(ans,-b[x]-b[y]);
    	else if(a[x]<a[y])
    		ckadd(ans,b[x]+b[y]);
    
    	add(x,a[x],-1,-b[x]);
    	add(y,a[y],-1,-b[y]);
    
    	swap(a[x],a[y]),swap(b[x],b[y]);
    
    	add(x,a[x],1,b[x]);
    	add(y,a[y],1,b[y]);
    }
    
    int main()
    {
    	n=read(),m=read();
    
    	for(RI i=1;i<=n;i++)
    		a[i]=read(),b[i]=read(),
    		add(i,a[i],1,b[i]);
    
    	for(RI i=n;i;i--)
    	{
    		ckadd(ans,b[i]*BIT_ask(a[i]-1,0)+BIT_ask(a[i]-1,1));
    		BIT_add(a[i],0,1),BIT_add(a[i],1,b[i]);
    	}
    
    	while(m--)
    	{
    		int x=read(),y=read();
    
    		turn(x,y);
    
    		printf("%lld
    ",ans);
    	}
    
    	return 0;
    }
    

    [ exttt{Thanks} exttt{for} exttt{watching} ]

  • 相关阅读:
    C# 委托的妙文相当适合我
    分享18个常用的网站性能测试工具
    使用Intelligencia.UrlRewriter重写有后缀及无后缀的url
    c#socket发送邮件详解
    欧美经典怀旧超级经典老歌!STARSHIP
    软件工程之百科名片SQA
    昨天订了道奇酷威
    陈淑桦梦醒时分
    TT已售
    组织过程资产与事业环境因素
  • 原文地址:https://www.cnblogs.com/cjtcalc/p/12431643.html
Copyright © 2011-2022 走看看