zoukankan      html  css  js  c++  java
  • P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并

    LINK:Druzyny

    这题研究了一下午 终于搞懂了.

    (n^2)的dp很容易得到.

    考虑优化.又有大于的限制又有小于的限制这个非常难处理.

    不过可以得到在限制人数上界的情况下能转移到的最远端点 且这个数组是单调的.

    而下界是随意的.

    这个可以利用单调队列做 也可以暴力线段树.

    然后考虑怎么优化 考虑CDQ分治 不过这里算是基于时间的分治吧.

    大体上就是求出来左边让左边对右边进行贡献.

    考虑这部分怎么做 左边为 ([l,mid])右边为(mid+1,r) 分别设两个决策(j,i)分属这两个集合.

    容易得到(left_ileq j,C_{j+1,i}leq i-j)后者的限制条件还是需要暴力转移.

    考虑对最大值进行分治 即对笛卡尔树上每个节点进行分治 这样C的条件就很明朗了.

    设分治中心为x 那么左区间为(l,x-1)右区间为(x,r)

    刚才的条件就变成了(left_ileq j,C_xleq i-j)

    考虑右边区间小的话就暴力扫右边区间 算出对应左区间 然后线段树上查区间最大值.

    左边小的话 暴力搜啊左边二分出(left)的合法端点 算出对应右区间 然后线段树上区间修改.

    可以发现上述暴力扫的过程类似启发式合并 这样复杂度为(nlog^2n)还不足以通过此题.

    正解是分类讨论了几种情况:

    首先我们知道 对于(left_i)是单调递增的 根据这个(left_i)可以划分三个区间.

    分别为(left_i<l,l<=left_i<x,x=<left_i)

    第三个可以忽略 第二个考虑这是一个横跨分治中心的 且在分治区间中的.

    每个i最多在一个分治区间中会发生这种情况.这个部分可以线段树暴力查区间最大值.所以复杂度为(nlogn)

    考虑第三种情况 这种情况下.

    继续分类讨论考虑左边大还是右边大.

    右边大的话 左边暴力扫 分两种情况 一种是 (i<x+C_x)这种情况可以在暴力扫的时候更新.一种是(i>=x+C_x)这种可以等右边扫完线段树区间修改.

    前者启发式合并(nlogn) 后者每个节点处至多进行一次复杂度(nlogn)

    左边大的话 右边暴力扫 先对右边最近节点扫到的j即(i-C_x)这个位置之前的位置线段树求max.然后再扫不断进行更新.

    前者每个节点至多进行一次 复杂度(nlogn) 后者还是启发式合并(nlogn)

    还有就是当前区间分成三段的节点可以二分.

    这样总复杂度为(nlogn) 可以通过本题.

    非常妙的分类讨论.

    需要注意的是 这题卡空间 不动态开点 利用id函数即可使得儿子之间有序标号.空间为(2n).

    code
    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<ctime>
    #include<cctype>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cmath>
    #include<cctype>
    #include<cstdlib>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<vector>
    #include<algorithm>
    #include<utility>
    #include<bitset>
    #include<set>
    #include<map>
    #define ll long long
    #define db double
    #define INF 1000000000
    #define inf 100000000000000000ll
    #define ldb long double
    #define pb push_back
    #define put_(x) printf("%d ",x);
    #define get(x) x=read()
    #define gt(x) scanf("%d",&x)
    #define gi(x) scanf("%lf",&x)
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define rep(p,n,i) for(RE int i=p;i<=n;++i)
    #define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
    #define fep(n,p,i) for(RE int i=n;i>=p;--i)
    #define vep(p,n,i) for(RE int i=p;i<n;++i)
    #define pii pair<int,int>
    #define mk make_pair
    #define RE register
    #define P 1000000007ll
    #define gf(x) scanf("%lf",&x)
    #define pf(x) ((x)*(x))
    #define uint unsigned long long
    #define ui unsigned
    #define EPS 1e-10
    #define sq sqrt
    #define S second
    #define F first
    #define mod 1000000007
    #define id(i,j) ((i+j)|(i!=j))
    #define max(x,y) ((x)<(y)?y:x)
    using namespace std;
    char *fs,*ft,buf[1<<15];
    inline char gc()
    {
    	return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
    	RE int x=0,f=1;RE char ch=gc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
    	return x*f;
    }
    const int MAXN=1000010;
    int n,top,rt;
    int c[MAXN],d[MAXN];
    int le[MAXN],s[MAXN];
    int ls[MAXN],rs[MAXN];
    struct wy
    {
    	int v,cnt;
    	wy(){v=-INF;cnt=0;}
    	wy(int _v,int _cnt){v=_v;cnt=_cnt;}
    	inline friend wy operator +(wy a,wy b)
    	{
    		if(a.v==-INF)return b;
    		if(b.v==-INF)return a;
    		if(a.v>b.v)return a;
    		if(b.v>a.v)return b;
    		return wy(a.v,a.cnt+b.cnt>=mod?a.cnt+b.cnt-mod:a.cnt+b.cnt);
    	}
    }cc;
    inline wy calc(wy b)	
    {
    	return b.v==-INF?b:wy(b.v+1,b.cnt);
    }
    wy tag[MAXN<<1],mx[MAXN<<1],f[MAXN];
    inline void build()
    {
    	top=0;
    	rep(1,n,i)
    	{
    		int now=0;
    		while(top&&c[s[top]]<=c[i])rs[s[top]]=now,now=s[top--];
    		s[++top]=i;ls[i]=now;
    	}
    	rt=s[1];
    	while(top>1)rs[s[top-1]]=s[top],--top;
    }
    inline void pushdown(int l,int r)
    {
    	int mid=(l+r)>>1;
    	int ww=id(l,r),w1=id(l,mid),w2=id(mid+1,r);
    	tag[w1]=tag[w1]+tag[ww];
    	mx[w1]=mx[w1]+tag[ww];
    	tag[w2]=tag[w2]+tag[ww];
    	mx[w2]=mx[w2]+tag[ww];
    	tag[ww]=f[n+1];
    }
    inline wy ask(int l,int r,int L,int R)
    {
    	if(L>R)return f[n+1];
    	if(L<=l&&R>=r)return mx[id(l,r)];
    	int mid=(l+r)>>1;
    	if(tag[id(l,r)].v>=0)pushdown(l,r);
    	if(R<=mid)return ask(l,mid,L,R);
    	if(L>mid)return ask(mid+1,r,L,R);
    	return ask(l,mid,L,R)+ask(mid+1,r,L,R);
    }
    inline void modify(int l,int r,int L,int R)
    {
    	if(L>R)return;
    	if(L<=l&&R>=r)
    	{
    		int ww=id(l,r);
    		mx[ww]=mx[ww]+cc;
    		tag[ww]=tag[ww]+cc;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(tag[id(l,r)].v>=0)pushdown(l,r);
    	if(R<=mid)modify(l,mid,L,R);
    	else 
    	{
    		if(L>mid)modify(mid+1,r,L,R);
    		else modify(l,mid,L,R),modify(mid+1,r,L,R);
    	}
    	mx[id(l,r)]=mx[id(l,mid)]+mx[id(mid+1,r)];
    }
    inline void solve(int l,int r,int x)
    {
    	if(l==r)
    	{
    		cc=f[l];modify(0,n,l,r);
    		f[l]=ask(0,n,l,r);
    		return;
    	}
    	solve(l,x-1,ls[x]);
    	int p=lower_bound(le+x,le+r+1,l)-le;
    	int q=lower_bound(le+x,le+r+1,x)-le;
    	if(x-l<r-x+1)
    	{
    		wy ww;
    		vep(l,x,i)
    		{
    			ww=ww+f[i];
    			if(i+c[x]>=x&&i+c[x]<p)
    				f[i+c[x]]=f[i+c[x]]+calc(ww);
    		}
    		cc=calc(ww);modify(0,n,x+c[x],p-1);
    	}
    	else
    	{
    		wy ww=ask(0,n,l,x-c[x]-1);
    		vep(x,p,i)
    		{
    			if(i-c[x]>=l&&i-c[x]<x)ww=ww+f[i-c[x]];
    			f[i]=f[i]+calc(ww);
    		}
    	}
    	vep(p,q,i)f[i]=f[i]+calc(ask(0,n,le[i],min(x-1,i-c[x])));
    	solve(x,r,rs[x]);
    }
    int main()
    {
    	//freopen("1.in","r",stdin);
    	get(n);
    	rep(1,n,i)get(c[i]),get(d[i]);
    	int h=1,t=0;
    	rep(1,n,i)
    	{
    		le[i]=le[i-1];
    		while(h<=t&&d[i]<=d[s[t]])--t;
    		s[++t]=i;
    		while(i-le[i]>d[s[h]])
    		{
    			++le[i];
    			if(le[i]>=s[h])++h;
    		}
    		//cout<<le[i]<<endl;
    	}
    	build();f[0]=wy(0,1);
    	solve(0,n,rt);
    	if(f[n].v==-INF)puts("NIE");
    	else printf("%d %d
    ",f[n].v,f[n].cnt);
    	return 0;
    }
    
  • 相关阅读:
    mysql改为mysqli几项注意
    修改链接服务器地址提高下载速度
    果然最适合码农的博客还是博客园
    mysql
    php 检测字符集
    Internet Download Manager has been registered with a fake Serial Number
    SVN图标不见了
    理解createTrackbar函数
    程序块结构
    数组初始化
  • 原文地址:https://www.cnblogs.com/chdy/p/13393782.html
Copyright © 2011-2022 走看看