• [Ynoi2019模拟赛]Yuno loves sqrt technology II


    题目大意:

    给定一个长为(n)的序列,(m)次询问,每次查询一个区间的逆序对数。

    32MB。

    解题思路:

    出题人题解

    众所周知lxl是个毒瘤,Ynoi道道都是神仙题

    二次离线莫队。

    对于每个区间([l,r]),考虑将右端点向右移动1格。

    其多出来的部分是([l,r])与(a_{r+1})产生的逆序对((a)与(b)产生的逆序对和(b)与(a)产生的逆序对是不同的)。

    显然,可以把([l,r])区间拆成([1,r]-[1,l-1])。

    所以,([l,r])与(a_{r+1})产生的逆序对=([1,r])与(a_{r+1})产生的逆序对-([1,l-1])与(a_{r+1})产生的逆序对。

    右端点不断移动,区间([1,r])始终在变,而([1,l-1])却是不变的。

    我们先考虑([1,r])与(a_{r+1})产生的逆序对,显然可以用树状数组在(O(nlog n))的时间内预处理出这个东西的前缀和。

    然后莫队端点移动、计算这部分贡献的时候就可以做到(O(1))。

    然后考虑([1,l-1])与(a_{r+1})产生的逆序对,假设(r)要移动到(r')((r< r' ))。

    那么,这部分产生的贡献其实是([1,l-1])与([r,r'])产生的逆序对。

    考虑用一个vector,(v_i)记录([1,i])这个区间与其他哪些区间会产生逆序对。

    由于莫队的复杂度证明,(v_i)记录的区间的长度总和不超过(O(nsqrt n)),所以直接扫描线,然后暴力计算贡献即可。

    其他端点移动方法也是类似处理一下即可。

    现在,我们需要一种数据结构,支持(O(1))查询前缀和,不超过(O(sqrt n))的时间进行单点修改。

    那么用权值分块即可,记录块的前缀和和每个位置到该位置所在块的块首的前缀和即可。

    由于我们计算的是每个询问与上一个询问的差,所以最后要进行一次前缀和。

    时间复杂度(O(nsqrt n+nlog n)),空间复杂度(O(n+m))。

    C++ Code:

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cctype>
    #include<cstring>
    #define N 100005
    #define siz 317
    class istream{
        char buf[15000003],*s;
        public:
            inline istream(){
                buf[fread(s=buf,1,15000001,stdin)]='
    ';
                fclose(stdin);
            }
            template<typename T>
            inline istream&operator>>(T&rhs){
                for(rhs=0;!isdigit(*s);++s);
                while(isdigit(*s))rhs=rhs*10+(*s++&15);
                return*this;
            }
    }cin;
    struct ostream{
        char buf[8000005],*s;
        inline ostream(){s=buf;}
        inline void operator<<(long long d){
            if(!d){
                *s++='0';
            }else{
                static long long w;
                for(w=1;w<=d;w*=10);
                for(;w/=10;d%=w)*s++=d/w^'0';
            }
            *s++='
    ';
        }
        inline ostream&operator<<(const char&c){*s++=c;return*this;}
        inline~ostream(){fwrite(buf,1,s-buf,stdout);}
    }cout;
    std::vector<int>ls;
    int n,m,a[N];
    long long L_R[N],R_L[N],ans[N],out[N];
    struct BiT{
    	int b[N];
    	inline void add(int i){for(;i<N;i+=i&-i)++b[i];}
    	inline int ask(int i){int x=0;for(;i;i^=i&-i)x+=b[i];return x;}
    }b;
    struct que{
    	int l,r,id;
    	inline bool operator<(const que&rhs)const{
    		return(l/siz!=rhs.l/siz)?(l<rhs.l):(r<rhs.r);
    	}
    }q[N];
    struct node{
    	int l,r,id,op;
    };
    std::vector<node>L[N],R[N];
    int bL[320],bR[320],bel[123456],c[123456],s[320];
    int main(){
    	ls.push_back(-1);
    	cin>>n>>m;
    	for(int i=1;i<=n;ls.push_back(a[i++]))cin>>a[i];
    	std::sort(ls.begin(),ls.end());
    	ls.erase(std::unique(ls.begin(),ls.end()),ls.end());
    	for(int i=1;i<=n;++i)a[i]=std::lower_bound(ls.begin(),ls.end(),a[i])-ls.begin();
    	for(int i=1;i<=n;++i){
    		L_R[i]=L_R[i-1]+b.ask(1e5)-b.ask(a[i]);
    		b.add(a[i]);
    	}
    	memset(b.b,0,sizeof b.b);
    	for(int i=n;i;--i){
    		R_L[i]=R_L[i+1]+b.ask(a[i]-1);
    		b.add(a[i]);
    	}
    	for(int i=1;i<=m;++i)cin>>q[i].l>>q[q[i].id=i].r;
    	std::sort(q+1,q+m+1);
    	q[0].l=1;
    	for(int i=1;i<=m;++i){
    		ans[i]=L_R[q[i].r]-L_R[q[i-1].r]+R_L[q[i].l]-R_L[q[i-1].l];
    		if(q[i].r>q[i-1].r)
    		R[q[i-1].l-1].push_back((node){q[i-1].r+1,q[i].r,i,-1});
    		else
    		if(q[i].r<q[i-1].r)
    		R[q[i-1].l-1].push_back((node){q[i].r+1,q[i-1].r,i,1});
    		if(q[i].l<q[i-1].l)
    		L[q[i].r+1].push_back((node){q[i].l,q[i-1].l-1,i,-1});
    		else
    		if(q[i].l>q[i-1].l)
    		L[q[i].r+1].push_back((node){q[i-1].l,q[i].l-1,i,1});
    	}
    	for(int i=1;i<=siz;++i){
    		bL[i]=bR[i-1]+1;
    		bR[i]=i*siz;
    		for(int j=bL[i];j<=bR[i];++j)bel[j]=i;
    	}
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<bel[a[i]];++j)++s[j];
    		for(int j=bL[bel[a[i]]];j<=a[i];++j)++c[j];
    		for(node j:R[i]){
    			int l=j.l,r=j.r;
    			long long tmp=0;
    			for(int k=l;k<=r;++k)
    			tmp+=s[bel[a[k]+1]]+c[a[k]+1];
    			ans[j.id]+=j.op*tmp;
    		}
    	}
    	memset(c,0,sizeof c);
    	memset(s,0,sizeof s);
    	for(int i=n;i;--i){
    		for(int j=bel[a[i]]+1;j<=siz;++j)++s[j];
    		for(int j=a[i];j<=bR[bel[a[i]]];++j)++c[j];
    		for(node j:L[i]){
    			int l=j.l,r=j.r;
    			long long tmp=0;
    			for(int k=l;k<=r;++k)
    			tmp+=s[bel[a[k]-1]]+c[a[k]-1];
    			ans[j.id]+=j.op*tmp;
    		}
    	}
    	for(int i=1;i<=m;++i)ans[i]+=ans[i-1],out[q[i].id]=ans[i];
    	for(int i=1;i<=m;++i)cout<<out[i];
    	return 0;
    }
    

      

  • 相关阅读:
    一个诡异的COOKIE问题
    PHP与JAVA构造函数的区别
    PHP获取上个月最后一天的一个容易忽略的问题
    jquery屏幕滚动计算事件总结
    Javascript 代理模式模拟一个文件同步功能
    Javascript实现HashTable类
    Javacript实现字典结构
    Javascript正则对象方法与字符串正则方法总结
    一个app,多个入口图标,activity-alias实现多程序入口并显示指定view完成
    javascript「篱式」条件判断
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/10016991.html
Copyright © 2020-2023  润新知