zoukankan      html  css  js  c++  java
  • bzoj 4540: [Hnoi2016]序列 莫队

    题目:

    给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
    1,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
    ≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
    6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。

    题解:

    首先这道题一看就是莫队.

    然后就不会了...
    校内胡测的第一题,可我不会啊...
    只打了个暴力上去

    首先这道题的难点在于拓展当前统计的答案区间.

    也就是说:

    在已经计算出来了区间([l,r])的情况下,如何使其拓展到([l,r+1])

    首先我们知道枚举区间再计算区间的价值是不明智的
    我们应该考虑枚举所有的元素,考虑其对区间的贡献.

    在上面的拓展中,我们设(left_i)表示(a_i)前面第一个比它小的值的下标.
    所以我们知道在拓展时新增的((r+1) - l + 1)个区间中
    左端点在([left_{r+1}+1,r+1])内的区间的价值一定都是(a_{i+1})
    所以现在我们的问题就是如何处理([l,left_{r+1}])这段区间的贡献了.
    我们设:(f[i][j])表示处理区间([i,j])所得到的贡献.
    那么我们有:
    (f[i][j] = f[i][left_j] + (j - left_j)*a_{j})
    我们发现实际上(i)只是限定了一个左端点而已.
    所以我们查询区间([l,r])时能够简单地取出(f[l][r])
    所以我们将(f)的第一个维度消去,即:
    (f[i] = f[left_i] + (i - left_i)*a_{i})
    这样我们在查询区间([l,r])时取出(f[r] - f[l-1])即可.

    于是我们在莫队的时候再xjb乱搞一下就好了.

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(ll &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    const ll maxn = 210010;
    ll loger[maxn],minn[maxn][26],a[maxn];
    ll minid[maxn][26];
    inline void pre(ll n){
    	loger[1] = 0;
    	for(ll i=2;i<=n;++i){
    		loger[i] = loger[i-1];
    		if( i == (1 << loger[i]+1)) ++ loger[i];
    	}
    	for(ll i=n;i>=1;--i){
    		minn[i][0] = a[i];
    		minid[i][0] = i;
    		for(ll j=1;i + (1<<j) - 1 <= n;++j){
    			minn[i][j] = min(minn[i][j-1],minn[i+(1<<j-1)][j-1]);
    			if(minn[i][j] == minn[i][j-1]) minid[i][j] = minid[i][j-1];
    			if(minn[i][j] == minn[i+(1<<j-1)][j-1]) minid[i][j] = minid[i+(1<<j-1)][j-1];
    		}
    	}
    }
    inline ll query(ll l,ll r){
    	ll k = loger[r-l+1];
    	if(minn[l][k] < minn[r-(1<<k)+1][k]) return minid[l][k];
    	return minid[r-(1<<k)+1][k];
    }
    ll belong[maxn];
    struct Node{
    	ll l,r,id;
    	bool friend operator < (const Node &a,const Node &b){
    		if(belong[a.l] == belong[b.l]) return a.r < b.r;
    		return belong[a.l] < belong[b.l];
    	}
    }q[maxn];
    ll anss[maxn],fl[maxn],fr[maxn];
    ll sta[maxn],top;
    inline void dp(ll n,ll *f){
    	sta[top = 1] = 0;
    	for(ll i=1;i<=n;++i){
    		while(a[sta[top]] > a[i]) -- top;
    		f[i] = (i - sta[top])*a[i] + f[sta[top]];
    		sta[++top] = i;
    	}
    }
    inline ll upd_R(ll l,ll r){
    	ll p = query(l,r+1);
    	return (p-l+1)*a[p] + fl[r+1] - fl[p];
    }
    inline ll upd_L(ll l,ll r){
    	ll p = query(l-1,r);
    	return (r-p+1)*a[p] + fr[l-1] - fr[p];
    }
    int main(){
    	ll n,Q;read(n);read(Q);a[0] = -(1LL<<60);
    	for(ll i=1;i<=n;++i) read(a[i]);
    	pre(n);dp(n,fl);reverse(a+1,a+n+1);//puts("reversed.");
    	dp(n,fr);reverse(a+1,a+n+1);reverse(fr+1,fr+n+1);
    	ll block = sqrt(n) + 1;
    	for(ll i=1;i<=n;++i) belong[i] = (i/block) + 1;
    	for(ll i=1;i<=Q;++i){
    		read(q[i].l);read(q[i].r);
    		q[i].id = i;
    	}sort(q+1,q+Q+1);
    	a[0] = 0;
    	ll L = 1,R = 1,ans = a[1];
    	for(ll i=1;i<=Q;++i){
    		while(R < q[i].r) ans += upd_R(L,R++);
    		while(R > q[i].r) ans -= upd_R(L,--R);
    		while(L > q[i].l) ans += upd_L(L--,R);
    		while(L < q[i].l) ans -= upd_L(++L,R);
    		anss[q[i].id] = ans;
    	}
    	for(ll i=1;i<=Q;++i){
    		printf("%lld
    ",anss[i]);
    	}
    	getchar();getchar();
    	return 0;
    }
    
  • 相关阅读:
    redis和memcache的区别
    c语言行编辑程序
    C语言栈的实现
    双向链表
    静态链表的合并
    静态链表的创建
    链表
    将非递减有序排列(L L1)归并为一个新的线性表L2 线性表L2中的元素仍按值非递减
    C语言合并两个集合(L,L1) 将L1中不在L中的元素插入到L线性表中
    oracle--JOB任务
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6576239.html
Copyright © 2011-2022 走看看