zoukankan      html  css  js  c++  java
  • [IOI2018] meetings 会议

    CXLIV.[IOI2018] meetings 会议

    被人坑了说这题是CDQ分治的题,一小时想不出来开了题解发现是道DP

    大概不会有人像我一样一开始想了极其诡异的DP,然后发现可以用莫队+树剖优化到 \(O(n\sqrt{n}\log^2n)\),但是这复杂度估计比 \(n^2\) 还差……

    扯远了,下面是正解。

    一个主流的想法是设 \(f[i,j]\) 表示 \([i,j]\) 区间的答案,然后考虑转移。

    考虑找到区间中的 \(\max\) 位置,不妨设为 \(k\)(如果有多个,随便取一个)。则,除非区间长为 \(1\),否则在 \(k\) 这个位置集合一定不是最优解法,因为此时该区间中所有人的费用都是区间中最大值 \(a_k\)。集合位置要么是 \(k\) 左侧某个位置,然后右边的人走过来,代价全部为 \(k\),要么相反。于是有

    \[f[i,j]=\max\Big\{f[i,k-1]+(j-k+1)a_k,f[k+1,j]+(k-i+1)a_k\Big\} \]

    (当然,对于 \(i>j\) 的状态,要么直接不予考虑,要么设其为 \(0\)

    发现这两边形式一致,就可以只考虑一半东西,然后另一半直接将序列翻转再做一遍即可得到。但需要注意的是,两边转移的 \(k\) 值应该相同(这针对于有多个最大值的情形)。

    我们不妨只考虑 \(f[k+1,j]+(k-i+1)a_k\) 这种转移。

    因为转移多次涉及到区间 \(\max\),所以有考虑笛卡尔树的可能。

    然后发现,所有的 \(k\) 都是笛卡尔树上的一个节点,且 \(j\) 在其右侧子树中。

    于是我们考虑求出从所有 \(k\) 开始,到其右侧子树中所有 \(j\)\(f[k+1,j]\) 值。

    因为我们明显不能显式地求出所有 \(f[k+1,j]\)(那样状态最劣仍是 \(O(n^2)\)),所以只能考虑借用旧的 \(f[k+1,j]\)。考虑用线段树维护。

    然后我们发现,用线段树维护简直是一个天才般的想法,因为我们发现,\([k+1,j]\) 这一段,刚好是 \(k\) 右儿子的区间。

    这就意味着 \(k\) 的DP值可以在右儿子的结果上稍加修改就得到。但这同样意味着,为了格式统一,\(k\) 要上传给它的父亲 \([i,j]\) 区间中,从 \(i\) 开始的所有值。

    于是我们现在考虑求出如何求出 \([i,j]\)

    我们蓦然发现,实际上已经有了一半的值,因为 \([i,k-1]\) 就是左儿子上传上来的DP值。而 \([i,k]\) 又可以由 \([i,k-1]\) 简单得到(准确地说,加上一个 \(a_k\) 就行了)。

    于是现在考虑应该如何在 \([k+1,j]\) 的基础上得到 \([i,j]\)

    祭出最上头的DP式,我们发现其有两种方式:一是左儿子中所有东西到右儿子去,右儿子中所有东西的DP值都要增加 \((k-l+1)a_k\),显然很好在线段树上区间加维护;二是右儿子中所有东西到左儿子去,这时候,位置 \(j\) 就要变成 \(f[i,k]+(j-k)a_k\),是等差数列的形式!

    但是我们悲哀地发现,要执行的是区间与等差数列取 \(\min\)。这显然不是线段树能办到的事。

    但是,转机来了:因为 \([k+1,j]\) 这段区间里所有元素都不大于 \(k\),所以当从 \(f[k+1,j]\)\(f[k+1,j+1]\) 时,二者间 \(\Delta\) 值是一定 \(\leq a_k\) 的!而上面的那个一次函数,相邻两个位置间的差是恒为 \(a_k\) 的。这就意味着,如果从某个位置开始区间加的结果首次比等差数列小了,则其右方的东西一定也小,左方的东西一定都大。

    因此,采取等差数列的部分一定是从 \(k+1\) 开始的连续区间。直接在线段树上二分就行了。

    时间复杂度 \(O(n\log n)\)。(但是常数极大)

    需要多说一句的是,这里的笛卡尔树没必要显式地搞出来,只需要求出每个节点所对应的区间就行了,直接用ST表即可。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,a[750010],st[750010][20],LG[750010],ql[750010],qr[750010];
    bool sec;//if we are executing the second round 
    ll res[750010];
    int MAX(int x,int y){if(!sec)return a[x]>a[y]?x:y;else return a[x]>=a[y]?x:y;}
    int RMQ(int x,int y){int k=LG[y-x+1];return MAX(st[x][k],st[y-(1<<k)+1][k]);}
    #define lson x<<1
    #define rson x<<1|1
    #define mid ((l+r)>>1)
    #define X x,l,r
    #define LSON lson,l,mid
    #define RSON rson,mid+1,r
    struct SegTree{ll tagl,tagadd,lval,rval;int tagdelta;}seg[3001000];
    void SETSEQ(int x,int l,int r,ll lval,int delta){seg[x].tagl=seg[x].lval=lval,seg[x].rval=lval+1ll*(r-l)*delta,seg[x].tagdelta=delta,seg[x].tagadd=0;}
    void ADD(int x,ll val){if(seg[x].tagl!=-1)seg[x].tagl+=val;else seg[x].tagadd+=val;seg[x].lval+=val,seg[x].rval+=val;}
    void pushup(int x){seg[x].lval=seg[lson].lval,seg[x].rval=seg[rson].rval;}
    void pushdown(int x,int l,int r){
    	if(seg[x].tagl!=-1){
    		SETSEQ(LSON,seg[x].tagl,seg[x].tagdelta);
    		SETSEQ(RSON,seg[x].tagl+1ll*seg[x].tagdelta*(mid-l+1),seg[x].tagdelta);
    		seg[x].tagl=seg[x].tagdelta=-1;
    	}else ADD(lson,seg[x].tagadd),ADD(rson,seg[x].tagadd),seg[x].tagadd=0;
    }
    void modifysequence(int x,int l,int r,int L,int R,ll leftval,int delta){
    	if(l>R||r<L)return;
    	if(L<=l&&r<=R){SETSEQ(X,leftval+1ll*(l-L)*delta,delta);return;}
    	pushdown(X),modifysequence(LSON,L,R,leftval,delta),modifysequence(RSON,L,R,leftval,delta),pushup(x);
    }
    void modifyadd(int x,int l,int r,int L,int R,ll val){
    	if(l>R||r<L)return;
    	if(L<=l&&r<=R){ADD(x,val);return;}
    	pushdown(X),modifyadd(LSON,L,R,val),modifyadd(RSON,L,R,val),pushup(x);
    }
    ll query(int x,int l,int r,int P){
    	if(l==r)return seg[x].lval;
    	pushdown(X);
    	if(P<=mid)return query(LSON,P);else return query(RSON,P);
    }
    int innerbinaryonseg(int x,int l,int r,ll lval,int delta){
    	if(l==r)return l;
    	pushdown(X);
    	if(seg[rson].lval<=lval+1ll*(mid+1-l)*delta)return innerbinaryonseg(LSON,lval,delta);
    	else return innerbinaryonseg(RSON,lval+1ll*(mid+1-l)*delta,delta);
    }
    int outerbinaryonseg(int x,int l,int r,int L,int R,ll lval,int delta){
    	if(l>R||r<L)return -1;
    	if(L<=l&&r<=R){
    //		printf("(%d,%d):%lld %lld\n",l,r,seg[x].lval,lval+1ll*(l-L)*delta);
    //		printf("(%d,%d):%lld %lld\n",l,r,seg[x].rval,lval+1ll*(r-L)*delta);
    		if(seg[x].rval>lval+1ll*(r-L)*delta)return -1;
    		if(seg[x].lval<=lval+1ll*(l-L)*delta)return l-1;
    		return innerbinaryonseg(X,lval+1ll*(l-L)*delta,delta);
    	}
    	pushdown(X);
    	int tmp=outerbinaryonseg(LSON,L,R,lval,delta);if(tmp!=-1)return tmp;
    	return outerbinaryonseg(RSON,L,R,lval,delta);
    }
    vector<int>v[750010];
    void iterate(int x,int l,int r){
    	if(l==r){printf("%lld ",seg[x].lval);return;}
    	pushdown(X),iterate(LSON),iterate(RSON);
    }
    void solve(int l,int r){
    	if(l==r){modifysequence(1,1,n,l,r,a[l],0);return;}
    	int o=RMQ(l,r);
    	if(l!=o)solve(l,o-1);
    	if(r!=o)solve(o+1,r);
    //	printf("[%d,%d]:%d\n",l,r,o);
    	for(auto i:v[o])res[i]=min(res[i],query(1,1,n,qr[i])+1ll*(o-ql[i]+1)*a[o])/*,printf("QQ:%d\n",query(1,1,n,qr[i])+1ll*(o-ql[i]+1)*a[o])*/;
    	ll FO;modifysequence(1,1,n,o,o,FO=(l==o?a[o]:query(1,1,n,o-1)+a[o]),0);
    	if(r!=o)modifyadd(1,1,n,o+1,r,1ll*(o-l+1)*a[o]);
    //	iterate(1,1,n),puts("");
    	if(r!=o){
    		int P=outerbinaryonseg(1,1,n,o+1,r,FO+a[o],a[o]);if(P==-1)P=r;//printf("%d\n",P);
    		if(P>=o+1)modifysequence(1,1,n,o+1,P,FO+a[o],a[o]);
    	}
    //	iterate(1,1,n),puts("");
    }
    void calc(){
    	for(int i=1;i<=n;i++)st[i][0]=i,v[i].clear();
    	for(int j=1;j<=LG[n];j++)for(int i=1;i+(1<<j)-1<=n;i++)st[i][j]=MAX(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    	for(int i=1;i<=m;i++){int o=RMQ(ql[i],qr[i]);if(o!=qr[i])v[o].push_back(i);}
    //	for(int i=1;i<=n;i++)printf("%d ",a[i]);puts("");
    	solve(1,n);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	for(int i=2;i<=n;i++)LG[i]=LG[i>>1]+1;
    	for(int i=1,l,r;i<=m;i++){
    		scanf("%d%d",&ql[i],&qr[i]),ql[i]++,qr[i]++;
    		if(ql[i]!=qr[i])res[i]=0x3f3f3f3f3f3f3f3f;else res[i]=a[ql[i]];
    	}
    	calc();
    	sec=true,reverse(a+1,a+n+1);for(int i=1;i<=m;i++)ql[i]=n-ql[i]+1,qr[i]=n-qr[i]+1,swap(ql[i],qr[i]);calc();
    	for(int i=1;i<=m;i++)printf("%lld\n",res[i]);
    	return 0;
    }
    

  • 相关阅读:
    iOS YSKit系列
    Xcode $(SRCROOT)和$(PROJECT_DIR)区别
    gcc -ldl 选项作用
    rpm 命令
    ls 命令详解
    Linux下用户组、文件权限详解
    服务不支持chkconfig的解决
    Linux 安装 MongoDB数据库
    CentOS 7 之前好好的,突然一天启动时黑屏,没有登陆界面了(配置 network-scripts 连网)
    vi 命令集
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601665.html
Copyright © 2011-2022 走看看