选举

### Description


  

Solution

  对于30%的数据我们可以很快得到一个简单的dp:令\(f[i]\)表示前\(i\)个人划分,差值的最大值,那么有:

\[f[i]=max(f[j]+val(sum(j+1,i)) \]

​  其中\(j\in[i-r,i-l]\)\(sum(j+1,i)\)表示\([j+1,i]\)这段区间内的和,\(val(x)\)函数当\(x<0\)时返回\(-1\),当\(x=0\)时返回\(0\),当\(x>0\)时返回\(1\)

  于是现在想办法优化这个dp式子

  区间这种东西。。还是会条件反射想到线段树qwq但是。。上面的这条式子并不够直观,那个\(val\)函数过于令人烦躁所以我们换一种更加简便的数学表示方式:

\[f[i]=max(f[j]+(s[i]>s[j])-(s[j]>s[i])) \]

​  (实际上。。也并不一定要拆成这样的形式(反正我是没有想到这样处理的了==),只要将其写成前缀和之差即可,只是觉得这种写法挺有意思就搬上来了)

  其中\(s[i]\)表示的是前\(i\)项的和,注意到一个比较重要的事情:\(s[i]\in [-n,n]\)的,同时每次转移根据\(s[i]\)\(s[j]\)的大小情况来分类的话。。需要考虑的情况其实只有大于小于等于三种

  那么假设现在没有\(l,r\)的限制,所以我们可以按照离散化之后的\(s\)建立线段树(具体一点就是。。种一棵权值线段树),这样的话我们就直接把后面那个很烦人的大于小于等于之类的值给解决了,因为这样处理之后,我们只要在线段树中维护每个\(s[i]\)值对应的\(f\)的最大值,然后在转移的时候(假设当前在计算\(f[x]\)),分别得到\(s<s[x]\)\(max(f)\)\(s=s[x]\)\(max(f)\)以及\(s>s[x]\)\(max(f)\),然后分别加上\(-1\)\(0\)\(1\),用来更新答案即可

  那么现在考虑加上\(l,r\)的限制,因为\(l,r\)是固定的,所以我们只要像一个窗口一样“滑动”就好了,每次将\(f[i-r-1]\)删掉,将\(f[i-l]\)加进去,最后一个问题就是这个\(max(f)\)怎么维护,放在线段树里面搞的话。。分分钟把人搞狗带吧==,所以我们直接对于每一个\(s[i]\)值维护一个单调队列处理即可,当最大值有变的时候再对线段树进行更新

  
  mark:比较重要的一点就是。。哪个部分最麻烦当然就是钦定它之后再搞别的部分啊。。。一直想着线段树线段树什么都丢进线段树是没有前途的

  mark:list是个好东西
   

​  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<list>
using namespace std;
const int N=1e6+10;
int n,m,L,R,Mn,Mx,inf;
namespace Seg{/*{{{*/
	const int N=::N*2*4;
	int ch[N][2],mx[N];
	int n,tot;
	void pushup(int x){mx[x]=max(mx[ch[x][0]],mx[ch[x][1]]);}
	void _build(int x,int l,int r){
		mx[x]=-inf;
		if (l==r) return;
		int mid=l+r>>1;
		ch[x][0]=++tot; _build(ch[x][0],l,mid);
		ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
	}
	void build(int _n){n=_n; tot=1; _build(1,1,n);}
	void _update(int x,int d,int lx,int rx,int delta){
		if (lx==rx){mx[x]=delta;return;}
		int mid=lx+rx>>1;
		if (d<=mid) _update(ch[x][0],d,lx,mid,delta);
		else _update(ch[x][1],d,mid+1,rx,delta);
		pushup(x);
	}
	void update(int d,int delta){_update(1,d,1,n,delta);}
	int _query(int x,int l,int r,int lx,int rx){
		if (l<=lx&&rx<=r) return mx[x];
		int mid=lx+rx>>1;
		if (r<=mid) return _query(ch[x][0],l,r,lx,mid);
		else if (l>mid) return _query(ch[x][1],l,r,mid+1,rx);
		else
			return max(_query(ch[x][0],l,mid,lx,mid),_query(ch[x][1],mid,r,mid+1,rx));
	}
	int query(int l,int r){return l>r?-inf:_query(1,l,r,1,n);}
}/*}}}*/
list<int> q[N*2];
int sum[N],f[N];
void add(int x){
	int v=sum[x];
	while (!q[v].empty()&&f[q[v].front()]<=f[x])
		q[v].pop_back();
	if (q[v].empty())
		Seg::update(v,f[x]);
	q[v].push_back(x);
}
void del(int x){
	int v=sum[x];
	if (q[v].front()==x){
		q[v].pop_front();
		if (q[v].empty())
			Seg::update(v,-inf);
		else
			Seg::update(v,f[q[v].front()]);
	}
}
void debug(){
	for (int i=Mn;i<=Mx;++i) printf("%d ",Seg::query(i,i));
	printf("\n");
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	int x,tmp1,tmp2,tmp3;
	scanf("%d%d%d",&n,&L,&R);
	inf=2*n;
	Mn=inf; Mx=-inf; sum[0]=n+1;
	for (int i=1;i<=n;++i){
		scanf("%d",&x);
		sum[i]=sum[i-1]+x;
		Mx=max(sum[i],Mx);
		Mn=min(sum[i],Mn);
	}
	Seg::build(Mx);
	for (int i=1;i<=n;++i){
		if (i-R>0)
			del(i-R-1);
		if (i-L>=0)
			add(i-L);
		f[i]=-inf;
		tmp1=Seg::query(1,sum[i]-1);
		tmp2=Seg::query(sum[i],sum[i]);
		tmp3=Seg::query(sum[i]+1,Mx);
		if (tmp1!=-inf)
			f[i]=max(f[i],tmp1+1);
		if (tmp2!=-inf)
			f[i]=max(f[i],tmp2);
		if (tmp3!=-inf)
			f[i]=max(f[i],tmp3-1);
		//debug();
	}
	if (f[n]==-inf) printf("Impossible\n");
	else printf("%d\n",f[n]);
}
posted @ 2018-10-10 16:52  yoyoball  阅读(140)  评论(0编辑  收藏  举报