1 条题解

  • 0
    @ 2022-4-16 9:11:04

    前言

    五发罚时害死人!

    莫队平衡复杂度差不多得了!


    题意

    给你两个长度为 nn 的自然数序列,aabb

    规定在 [l,r][l,r] 范围内的平衡操作,是指自由选出一段长度为偶数 kk 的整数序列 pospos,使得 lpos1<pos2<<poskrl\le pos_1<pos_2<\dots<pos_k\le r,然后令 pos1,pos3,pos5,pos7,,posk1pos_1,pos_3,pos_5,pos_7,\dots,pos_{k-1} 位置的 aa 加一,令 pos2,pos4,pos6,pos8,,poskpos_2,pos_4,pos_6,pos_8,\dots,pos_k 位置的 bb 加一。

    qq 次询问,每次询问给你一段区间 [l,r][l,r],问你至少做几次该区间内的平衡操作,可以使 [l,r][l,r] 间所有位置满足 a=ba=b,无解输出 1-1。询问独立。

    数据范围:2n1052\le n\le10^51q1051\le q\le10^50ai,bi1090\le a_i,b_i\le10^91l<rn1\le l<r\le n


    思路

    转化题意

    A=abA=a-b,则题意转化为:

    给你一个长度为 nn 的整数序列 AA

    规定在 [l,r][l,r] 范围内的平衡操作,是指自由选出一段长度为偶数 kk 的整数序列 pospos,使得 lpos1<pos2<<poskrl\le pos_1<pos_2<\dots<pos_k\le r,然后令 pos1,pos3,pos5,pos7,,posk1pos_1,pos_3,pos_5,pos_7,\dots,pos_{k-1} 位置的 AA 加一,令 pos2,pos4,pos6,pos8,,poskpos_2,pos_4,pos_6,pos_8,\dots,pos_k 位置的 AA 减一。

    qq 次询问,每次询问给你一段区间 [l,r][l,r],问你至少做几次该区间内的平衡操作,可以使 [l,r][l,r] 间所有位置满足 A=0A=0,无解输出 1-1。询问独立。

    数据范围:2n1052\le n\le10^51q1051\le q\le10^5109Ai109-10^9\le A_i\le10^91l<rn1\le l<r\le n


    分析

    有了 AA 数组还不够,我们依旧需要一个条件来判断是否有解。

    I.显然若区间内 AA 数组的和不是 00,则必然无解。

    II.同时,若区间内存在前缀和为正数 mm ,则亦无解。

    证明:

    由每次操作对前缀和的影响一定不减且最多加一可得。

    因为序列 $D:\big<0,\dots,0,1,0,\dots,0,-1,0,\dots,0,1,0,\dots,0,-1,0,\dots\big>$ 序列的前缀和序列 D\sum D 中每一项不是 00 就是 11,由前缀和的线性性即 (A+D)=A+D\sum(A+D)=\sum A+\sum D ,这个 mm 必然不减而无法变为 00

    除去这两种情况,剩下的情况都容易构造方案,即利用上述论述中操作对前缀和的影响($\sum D:\big<0,0,0,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,\dots\big>$)来构造,构造方案略。

    III.不难发现答案即区间内最大的前缀和的相反数,或者说区间内最小前缀和的相反数,即 min{A}-\min\{\sum A\},证明由构造方案与证明下界两部分组成,此处略去。


    考虑怎么快速判断。

    对情况 I,直接维护全局前缀和数组即可。

    对情况 II,等价于区间内最大前缀和为正,这个可以转化成序列上两个前缀和的差,于是就要维护全局前缀和数组,要求查询区间 max\max,用 ST 表即可。

    对情况 III,等价于求区间内最小前缀和,这个也可以转化成序列上两个前缀和的差,于是就要维护全局前缀和数组,要求查询区间 min\min,用 ST 表即可。

    复杂度 O(nlogn)O(1)O(n\log n) - O(1)


    由于赛前在练习莫队,于是赛场上没有想到 ST 表,胡了一个奇妙的平衡复杂度莫队做法,大致方法类似,就是将全局前缀和数组先离散化再插入一个值域分块里面,反正是个非常谔谔的做法。

    复杂度 O(nn)O(n\sqrt n)

    结果拿了 55 发罚时,写了亿年才过(


    Code

    给出平衡复杂度莫队版的代码。

    // Problem: E. Equilibrium
    // Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
    // URL: https://codeforces.com/contest/1556/problem/E
    // Memory Limit: 256 MB
    // Time Limit: 2000 ms
    
    #include <algorithm>
    #include <math.h>
    #include <set>
    #include <map>
    #include <stdio.h>
    typedef long long llt;
    typedef unsigned uint;typedef unsigned long long ullt;
    typedef bool bol;typedef char chr;typedef void voi;
    typedef double dbl;
    template<typename T>bol _max(T&a,T b){return(a<b)?a=b,true:false;}
    template<typename T>bol _min(T&a,T b){return(b<a)?a=b,true:false;}
    template<typename T>T power(T base,T index,T mod){return((index<=1)?(index?base:1):(power(base*base%mod,index>>1,mod)*power(base,index&1,mod)))%mod;}
    template<typename T>T lowbit(T n){return n&-n;}
    template<typename T>T gcd(T a,T b){return b?gcd(b,a%b):a;}
    template<typename T>T lcm(T a,T b){return(a!=0||b!=0)?a/gcd(a,b)*b:(T)0;}
    template<typename T>T exgcd(T a,T b,T&x,T&y){if(!b)return y=0,x=1,a;T ans=exgcd(b,a%b,y,x);y-=a/b*x;return ans;}
    template<const uint Sqrt=1500>
    struct Block
    {
    	uint A[Sqrt*Sqrt],B[Sqrt];
    	Block()
    	{
    		for(uint i=0;i<Sqrt*Sqrt;i++)A[i]=0;
    		for(uint i=0;i<Sqrt;i++)B[i]=0;
    	}
    	voi insert(uint v){A[v]++,B[v/Sqrt]++;}
    	voi erase(uint v){A[v]--,B[v/Sqrt]--;}
    	uint findmin()
    	{
    		for(uint i=0;i<Sqrt;i++)if(B[i])
    			for(uint j=0;j<Sqrt;j++)if(A[i*Sqrt+j])return i*Sqrt+j;
    		return-1;
    	}
    	uint findmax()
    	{
    		for(uint i=Sqrt-1;~i;i--)if(B[i])
    			for(uint j=Sqrt-1;~j;j--)if(A[i*Sqrt+j])return i*Sqrt+j;
    		return-1;
    	}
    };
    Block<>B;
    uint Siz;
    struct ques{uint l,r,t;llt ans;};
    bol cmp1(const ques&a,const ques&b){return(a.l/Siz==b.l/Siz)?(((a.l/Siz)&1)?b.r<a.r:(a.r<b.r)):(a.l<b.l);}
    bol cmp2(const ques&a,const ques&b){return a.t<b.t;}
    llt A[100005],_S[100005],User[100005];
    uint S[100005];
    ques Q[100005];
    uint l,r;
    std::set<llt>Set;
    std::map<llt,uint>Map;
    llt sgn(llt v){return(v>0)?1:(v?-1:0);}
    voi insert(uint m){B.insert(S[m+1]);}
    voi erase(uint m){B.erase(S[m+1]);}
    int main()
    {
    	uint n,m;llt v;
    	scanf("%u%u",&n,&m),Siz=sqrt(n);
    	for(uint i=0;i<n;i++)scanf("%lld",A+i);
    	for(uint i=0;i<n;i++)scanf("%lld",&v),Set.insert(_S[i+1]=_S[i]+(A[i]-=v));
    	Set.insert(0);
    	while(!Set.empty())User[Map[*Set.rbegin()]=Set.size()]=*Set.rbegin(),Set.erase(*Set.rbegin());
    	for(uint i=0;i<=n;i++)S[i]=Map[_S[i]];
    	for(uint i=0;i<m;i++)scanf("%u%u",&Q[i].l,&Q[i].r),Q[i].l--,Q[i].t=i;
    	std::sort(Q,Q+m,cmp1),l=r=0;
    	for(uint i=0;i<m;i++)
    	{
    		ques&qs=Q[i];
    		while(l>qs.l)insert(l-1),l--;
    		while(r<qs.r)insert(r),r++;
    		while(l<qs.l)erase(l),l++;
    		while(r>qs.r)erase(r-1),r--;
    		qs.ans=(B.findmax()==S[l]&&S[r]==S[l])?User[S[l]]-User[B.findmin()]:-1;
    	}
    	std::sort(Q,Q+m,cmp2);
    	for(uint i=0;i<m;i++)printf("%lld\n",Q[i].ans);
    	return 0;
    }
    
    • 1

    信息

    ID
    7273
    时间
    2000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    2
    上传者