• 【洛谷4501】[ZJOI2018] 胖(二分+RMQ)


    点此看题面

    • 有一条长度为(n)的链(点编号为(1sim n)),链上每条边有一个边权。
    • (q)次询问,每次给出(k_i)条从(0)号点连出的对应节点各不相同的边,问这张图上跑(Bellman-Ford)算法的复杂度。
    • (n,q,sum k_ile2 imes10^5)

    首先考虑二分答案

    我们单独考虑(0)号点连出的每一条边,那么对应点能够更新到的点必然是一段连续的区间。

    所以容易想到二分答案。

    如果想直接线段树上二分,就会发现这有些困难,因为有些离当前点距离近但是点数比较多的点是无法造成影响的。

    因此我们只能考虑二分答案之后再验证,尽管这样一来复杂度是两个(log),但还是能过的。

    (RMQ)

    说起来好久没写过(RMQ)了。。。

    其实我首先想到的是线段树,然而听说可能会(T)掉(讲道理由于仍然需要使用(lower\_bound)(upper\_bound),复杂度应该没有区别),因此就去写了个(RMQ)

    其实就是考虑对于当前二分到的点(mid)来说,第(i)条边到(mid)的距离无非两种情况:

    • (p_ile mid)(l_i+d_{p_i}-d_{mid})
    • (p_i>mid)(l_i-d_{p_i}+d_{mid})

    所以我们只要开两个(RMQ),分别维护出(l_i+d_{p_i})(l_i-d_{p_i})的最小值即可。

    注意两条边同时到达某一个点的情况要特殊讨论,在距离相同的时候注意不要重复计算。

    代码:(O(nlog^2n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    #define LN 20
    #define LL long long
    #define LB(x) (lower_bound(s+1,s+k+1,Data(x))-s)
    #define UB(x) (upper_bound(s+1,s+k+1,Data(x))-s)
    using namespace std;
    int n,k,LG[N+5];LL d[N+5];
    struct Data
    {
    	int p,v;I Data(CI x=0,CI y=0):p(x),v(y){}
    	I bool operator < (Con Data& o) Con {return p<o.p;}
    }s[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    struct RMQ
    {
    	LL V[N+5][LN+5];I void Init()//初始化
    	{
    		for(RI j=1,i;(1<<j)<=k;++j) for(i=1;i+(1<<j)-1<=k;++i) V[i][j]=min(V[i][j-1],V[i+(1<<j-1)][j-1]);
    	}
    	I LL Q(CI l,CI r) {if(l>r) return 1e18;RI t=LG[r-l+1];return min(V[l][t],V[r-(1<<t)+1][t]);}//询问区间最值
    }A,B;
    int main()
    {
    	RI Qt,i,j;for(F.read(n),F.read(Qt),LG[0]=-1,i=1;i<=n;++i) LG[i]=LG[i>>1]+1;//预处理log2
    	RI l,r,x,mid;LL t;for(i=2;i<=n;++i) F.read(d[i]),d[i]+=d[i-1];W(Qt--)
    	{
    		for(F.read(k),i=1;i<=k;++i) F.read(s[i].p),F.read(s[i].v);sort(s+1,s+k+1),s[k+1].p=0;//一个坑点,因为我用了lower_bound,故要把s[k+1]清零
    		for(i=1;i<=k;++i) A.V[i][0]=s[i].v+d[s[i].p],B.V[i][0]=s[i].v-d[s[i].p];//初始化RMQ
    		for(A.Init(),B.Init(),t=0,i=1;i<=k;++i)//枚举每条边计算贡献
    		{
    			#define D(i,w) (abs(d[s[i].p]-d[w])+s[i].v)//计算第i条边到点w的距离
    			l=1,r=x=s[i].p;W(mid=l+r-1>>1,l^r)//二分左边界
    			{
    				if(s[j=LB(2*mid-x)].p==2*mid-x&&D(j,mid)<=D(i,mid)) {l=mid+1;continue;}//特判两点同时到达
    				min(A.Q(LB(mid),i-1)-d[mid],B.Q(UB(2*mid-x),LB(mid)-1)+d[mid])>D(i,mid)?r=mid:l=mid+1;//判断
    			}t+=x-r+1;//统计左半部分答案
    			l=s[i].p,r=n;W(mid=l+r+1>>1,l^r)
    			{
    				if(s[j=LB(2*mid-x)].p==2*mid-x&&D(j,mid)<D(i,mid)) {r=mid-1;continue;}//特判两点同时到达,这里不能再写=
    				min(A.Q(LB(mid),LB(2*mid-x)-1)-d[mid],B.Q(i+1,LB(mid)-1)+d[mid])>D(i,mid)?l=mid:r=mid-1;//判断
    			}t+=l-x;//统计右半部分答案
    		}F.writeln(t);
    	}return F.clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    pip错误:'utf-8' codec can't decode byte解决方法
    windows中python2与python3共存
    Alpha 冲刺 (3/10)
    Alpha 冲刺 (2/10)
    Alpha 冲刺 (1/10)
    项目需求分析报告答辩总结
    项目选题报告答辩总结
    项目UML设计(团队)
    # 第七次作业--项目需求分析(团队)
    结对项目--第二次作业
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4501.html
Copyright © 2020-2023  润新知