zoukankan      html  css  js  c++  java
  • BZOJ

    题目

    传送门

    解法

    无论多么麻烦的代码写完过后都要耐着性子检查。

    首先有朴素 (n^2)(mathtt{DP})

    虽然不知道是怎么想到的,但是可以尝试思考一下 (mathtt{cdq}) 分治。考虑在询问 (i) 第一次覆盖区间 ( ext{mid}) 时计算这个询问。即当二分到区间 ([l,r]) 时,我们先 (mathtt{DP}) 这个区间,再通过得到的值算出询问答案。为什么这是正确的?其实也就是为什么 ([l,r]) 一定覆盖这个询问?容易想到如果 ([l,r]) 未覆盖询问 (i),那么 (l,r) 中的一个一定在询问 (i) 的区间范围内,而这个在区间范围内的 (l / r) 一定可以向外扩展,也就是说一定是之前的一个 ( ext{mid}),所以假设不成立。

    可是如果我们朴素 (mathtt{DP}) 显然还是过不了的。

    发现朴素 (mathtt{DP}) 复杂度那么高是因为要保证选取的数大于等于 (l),小于等于 (r) 这两个条件,如果我们把这两个条件拆开做,再合并起来会怎样呢?看上去十分不可做。

    不过这道题我们可以保证 (l_ile ext{mid},r_i> ext{mid})。我们就可以以 ( ext{mid}) 为界分别 (mathtt{DP}) 两类条件。如何合并?显然需要枚举穿过 ( ext{mid})(L) 条语录,由于这道题 (L) 特别小,所以方法可行。

    时间复杂度 (mathcal O(nLlog n))

    但是细节贼多。

    代码

    #include <cstdio>
    #define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
    #define fep(i,_l,_r) for(signed i=(_l),_end=(_r);i>=_end;--i)
    #define print(x,y) write(x),putchar(y)
    
    int read() {
    	int x=0,f=1; char s;
    	while((s=getchar())>'9' || s<'0') if(s=='-') f=-1;
    	while(s>='0' && s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    	return x*f;
    }
    
    void write(int x) {
    	if(x<0) return (void)(putchar('-'),write(-x));
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <iostream>
    using namespace std;
    
    const int maxn=1e5+5;
    
    int n,L,a[maxn],m,f[55][maxn],g[55][maxn],ans[maxn];
    struct node {int l,r,id;} q[maxn],tl[maxn],tr[maxn];
    
    void calcL(int l,int r) {
    	rep(i,0,min(r-l,L)) { // 枚举中间那 L 条语录在 [l,mid] 中有多少条
    		f[i][r-i+1]=0;
    		fep(j,r-i,l)
    			f[i][j]=max(f[i][j+1],(j+L-1>r-i?0:f[i][j+L]+a[j+L-1]));
    	}
    }
    
    void calcR(int l,int r) {
    	rep(i,0,min(r-l,L)) {
    		g[i][l+i-1]=0;
    		rep(j,l+i,r)
    			g[i][j]=max(g[i][j-1],(j-L+1<l+i?0:g[i][j-L]+a[j]));
    	}
    }
    
    void dicon(int l,int r,int ql,int qr) {
    	if(ql>qr || r-l+1<L) return;
    	int mid=l+r>>1,totl=0,totr=0;
    	calcL(l,mid),calcR(mid+1,r);
    	rep(i,ql,qr) 
    		if(q[i].l>mid) tr[++totr]=q[i];
    		else if(q[i].r<=mid) tl[++totl]=q[i];
    		else {
                if(q[i].l<l || q[i].r>r) puts("fuck bitch!");
    			ans[q[i].id]=max(f[0][q[i].l]+g[0][q[i].r],0);
    			rep(j,max(1,L+mid-q[i].r),min(mid-q[i].l+1,L)) 
    				ans[q[i].id]=max(ans[q[i].id],f[j][q[i].l]+g[L-j][q[i].r]+a[mid+L-j]);
    			// mid-j+1>=l -> j<=mid-l+1
    			// L-j+mid<=r -> j>=L+mid-r
    		}
    	rep(i,1,totl) q[ql+i-1]=tl[i];
    	rep(i,1,totr) q[ql+totl+i-1]=tr[i];
    	dicon(l,mid,ql,ql+totl-1),dicon(mid+1,r,ql+totl,ql+totl+totr-1);
    }
    
    int main() {
    	n=read(),L=read();
    	rep(i,1,n) a[i]=a[i-1]+read();
    	fep(i,n,L) a[i]-=a[i-L];
    	m=read();
    	rep(i,1,m) q[i].l=read(),q[i].r=read(),q[i].id=i;
    	dicon(1,n,1,m);
    	rep(i,1,m) print(ans[i],'
    ');
    	return 0;
    }
    
  • 相关阅读:
    IOS触摸事件
    IOS中内存管理机制浅解
    Info.plist与Prefix.pch修改文件位置遇到的问题及解决方法
    block使用小结、在arc中使用block、如何防止循环引用
    __block的作用
    ios中键值编码kvc和键值监听kvo的特性及详解
    ios多线程开发的常用三种方式
    Xcode7连接网络设置
    不能弹出虚拟键盘的解决办法
    【测试工具】moco入门(一)
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14483515.html
Copyright © 2011-2022 走看看