zoukankan      html  css  js  c++  java
  • [POI2011]Lightning Conductor

    题面在这里

    description

    已知一个长度为(n)的序列(a_1,a_2,...,a_n)

    对于每个(1le ile n),找到最小的非负整数(p),
    满足对于任意的(1le jle n),(a_jle a_i+p-sqrt{|i-j|})

    data range

    [nle 5 imes 10^5,a_ile 10^9 ]

    solution

    绝对值怎么办?
    我们先从左到右(DP j< i)的部分(此时有(|i-j|=i-j)),
    再右到左(DP j> i)的部分(此时有(|i-j|=j-i));

    那么我们现在考虑这个式子:

    [f[i]=max_{j=1}^{i-1}(a[j]-a[i]+sqrt{i-j}) ]

    对于每一个相同的(i),
    (w[j]=sqrt{i-j}),那么我们很容易发现这个函数是凸函数
    对于两个决策(j<k),当((j-k))一定时,我们发现(w[j]-w[k])随着(i)的递增而递减
    而对于这两个决策,(j)(k)优当且仅当对于(j<k<ile n),

    [a[j]+sqrt{i-j}ge a[k]+sqrt{i-k} ]

    [g[j,k]=a[j]-a[k]+w[j]-w[k]ge 0 ]

    (i)递增时,(g[j,k])这个函数单调递减,那么存在一个分界点(i'),
    使得当(ile i')时,(g[j,k]ge 0)((j)更优),当(ige i')时,(g[j,k]le 0)((k)更优);

    那么我们知道这个(DP)方程具有决策单调性
    具体如何实现?

    考虑相邻的两个决策(j,k),
    由于上面的性质,我们可以二分出这两个决策的分界点(t[j,k]);
    对于相邻的三个决策(x,y,z),如果(t[x,y]ge t[y,z])那么决策(y)是没有用的可以扔掉

    考虑维护一个单调队列,存放所有合法的决策;
    由于我们在维护的同时(i)是变化的,
    因此我们需要求出每个决策此时转移最优的区间
    即单调队列存放的元素为((pos,l_{pos},r_{pos}))(注意(pos < l_{pos}le r_{pos}))

    当加入新决策(j)时,考虑队尾的决策((k,l_k,r_k));
    如果(j)总是比(k)要劣(在这里指对于(i=n)仍有(a[j]+sqrt{i-j}le a[k]+sqrt{i-k})),
    则不考虑决策(j);
    否则(此时(r_j=n)),如果对于(i=l_k)(a[j]+sqrt{i-j}ge a[k]+sqrt{i-k}),
    一直弹掉队尾决策并考虑队尾的前一个决策,如果队列为空则压入((j,j,n));
    否则(此时存在一个决策(k)无法被弹出),考虑二分决策(k)(j)的分界点(mid)
    (本人的定义是,对于(ile mid),有(a[k]+sqrt{i-k}> a[j]+sqrt{i-j})),
    之后将(r_k)修改为(mid),压入决策((j,mid,n))

    il dd calc(int j,int i){return a[j]-a[i]+sqrt(i-j);}
    //计算j转移至i时对应的DP值
    il int divide(int k,int j,int l,int r){
    	RG int mid,ans=l;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(calc(k,mid)>calc(j,mid))
    			ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return ans;
    }
    //二分分界点
    il void push(int i){
    	if(calc(Qx[R],n)>calc(i,n))return;
    	while(calc(Qx[R],Ql[R])<calc(i,Ql[R])&&L<=R)R--;
    	if(L>R){Ql[++R]=i;Qr[R]=n;Qx[R]=i;return;}
    	Qr[R]=divide(Qx[R],i,Ql[R],Qr[R]);
    	R++;Ql[R]=Qr[R-1]+1;Qr[R]=n;Qx[R]=i;
    }
    //插入新决策
    

    当要查询位置(pos)时,直接从队头开始查找,
    如果队首决策((k,l_k,r_k))(r_k<pos)或队首决策劣于队首后决策则将其弹出;
    否则,将其作为此次查询的答案

    il int pop(int i){
    	while(Qr[L]<i||(L<R&&calc(Qx[L],i)<calc(Qx[L+1],i)))L++;
    	return Qx[L];
    }
    //查询答案
    

    这里是总的(DP)模板

    il void DP(int *a,int *p,int n){
    	L=0;R=-1;
    	for(RG int i=1;i<=n;i++){
    		if(i!=1)p[i]=ceil(calc(pop(i),i));Ql[L]=i;push(i);
    		//答案为非负整数,这里做了上取整
    	}
    }
    //DP序列
    

    code

    #include<bits/stdc++.h>
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<iomanip>
    #include<cstring>
    #include<complex>
    #include<vector>
    #include<cstdio>
    #include<string>
    #include<bitset>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<map>
    #include<set>
    #define mp make_pair
    #define pub push_back
    #define puf push_front
    #define pob pop_back
    #define pof pop_front
    #define RG register
    #define il inline
    using namespace std;
    typedef unsigned long long ull;
    typedef vector<int>VI;
    typedef long long ll;
    typedef double dd;
    const dd eps=1e-10;
    const int N=1000000;
    const int inf=1e9+7;
    il ll read(){
    	RG ll data=0,w=1;RG char ch=getchar();
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
    	return data*w;
    }
    
    il void file(){
    	freopen("a.in","r",stdin);
    	freopen("a.out","w",stdout);
    }
    
    int n,a[N],p[N],s[N];
    int Qx[N],Ql[N],Qr[N],L,R;
    
    il dd calc(int j,int i){return a[j]-a[i]+sqrt(i-j);}
    //计算j转移至i时对应的DP值
    il int divide(int k,int j,int l,int r){
    	RG int mid,ans=l;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(calc(k,mid)>calc(j,mid))
    			ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return ans;
    }
    //二分分界点
    il void push(int i){
    	if(calc(Qx[R],n)>calc(i,n))return;
    	while(calc(Qx[R],Ql[R])<calc(i,Ql[R])&&L<=R)R--;
    	if(L>R){Ql[++R]=i;Qr[R]=n;Qx[R]=i;return;}
    	Qr[R]=divide(Qx[R],i,Ql[R],Qr[R]);
    	R++;Ql[R]=Qr[R-1]+1;Qr[R]=n;Qx[R]=i;
    }
    //插入新决策
    il int pop(int i){
    	while(Qr[L]<i||(L<R&&calc(Qx[L],i)<calc(Qx[L+1],i)))L++;
    	return Qx[L];
    }
    //查询答案
    il void DP(int *a,int *p,int n){
    	L=0;R=-1;
    	for(RG int i=1;i<=n;i++){
    		if(i!=1)p[i]=ceil(calc(pop(i),i));Ql[L]=i;push(i);
    	}
    }
    //DP序列
    int main()
    {
    	n=read();
    	for(RG int i=1;i<=n;i++)a[i]=read();
    	DP(a,p,n);reverse(a+1,a+1+n);DP(a,s,n);
    	for(RG int i=1;i<=n;i++)
    		printf("%d
    ",max(0,max(p[i],s[n-i+1])));
    	//记得答案是非负整数
    	return 0;
    }
    
    
  • 相关阅读:
    go---weichart个人对Golang中并发理解
    go语言值得学习的开源项目推荐
    mysql17---增量备份
    mysql16---读写分离
    mysql15--垂直分表水平分表
    mysql14---手动备份
    mysql13---索引使用注意
    mysql12----explain
    mysql11---主键普通全文索引
    OpenOffice的简单安装
  • 原文地址:https://www.cnblogs.com/cjfdf/p/8870067.html
Copyright © 2011-2022 走看看