1. 程式人生 > >選舉 - 線段樹 -單調佇列 - dp

選舉 - 線段樹 -單調佇列 - dp

題目大意:
給定序列a,滿足 a i { 1 , 0 ,

1 } a_i\in\{-1,0,1\} ;定義 w ( l , r )
= s g n ( i = l
r
a i ) w(l,r)=sgn(\sum_{i=l}^r a_i)
,其中sgn為符號函式。將a劃分成若干長度在[l,r]之間的段,使得每一段的權值之和最大。 n 1 0 6 n\le10^6
題解:
寫了個nlgn得了90,真……
nlgn做法是,考慮:
f i = max j [ i r , i l ] d p j + s g n ( S i S j ) f_i=\max_{j\in[i-r,i-l]}dp_j+sgn(S_i-S_j)
於是你只關心 S i S_i S j S_j 的大小關係,對S建立線段樹,維護那些S相等的j關於f的單調佇列即可,這樣就是O(nlgn)。
線性做法是,考慮f和S的值域都是O(n)級別的,且 w ( l , r ) 1 |w(l,r)|\le1 ,因此在計算f的時候,只有最大的 f j f_j f j 1 f_j-1 才是有用的,那麼對於這兩個數值,顯然字首和越小越好,因此維護O(n)個單調佇列,每個單調佇列是f值確定時關於S的單調佇列,最後全域性搞一個關於f的單調佇列來維護這個區間最大即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define N 2000010
#define inf -100000000
using namespace std;typedef pair<int,int> pii;typedef set<int>::iterator sit;
inline int inn()
{
	int x=0,s=1,ch;while(((ch=gc)<'0'||ch>'9')&&ch!='-');if(ch^'-') x=ch^'0';else s=-1;
	while((ch=gc)>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return s*x;
}
int bas,dp[N],s[N],a[N];inline int sgn(int x) { return (x<0)?-1:(x>0); }
struct Qmax{
	int fp,rp;vector<pii> q; Qmax() { fp=0,rp=-1; }
	inline void push(pii t) { q.resize((int)q.size()+1);while(fp<=rp&&t.fir>=q[rp].fir) rp--;q[++rp]=t; }
	inline int front() { return fp<=rp?q[fp].fir:inf; } inline void pop(int t) { fp+=(fp<=rp&&q[fp].sec==t); }
}q;
struct Qmin{
	Qmax q;inline void push(pii t) { q.push(mp(-t.fir,t.sec)); }
	inline int front() { return -q.front(); } inline void pop(int t) { q.pop(t); }
}f[N];
inline int ins(int x) { if(x<0||dp[x]+bas<=0) return 0;return q.push(mp(dp[x],x)),f[dp[x]+bas].push(mp(s[x],x)),0; }
inline int del(int x) { if(x<0||dp[x]+bas<=0) return 0;return q.pop(x),f[dp[x]+bas].pop(x),0; }
int main()
{
	int n=inn(),l=inn(),r=inn();bas=n+1;rep(i,1,n) a[i]=inn(),s[i]=s[i-1]+a[i];
	for(int i=1;i<=n;i++)
	{
		dp[i]=inf,del(i-r-1),ins(i-l);int v=q.front(),s1=-inf,s2=-inf;
		if(v+bas>0) s1=f[v+bas].front();if(s1<-inf) dp[i]=max(dp[i],v+sgn(s[i]-s1));
		if(v+bas-1>0) s2=f[v+bas-1].front();if(s2<-inf) dp[i]=max(dp[i],v-1+sgn(s[i]-s2));
	}
	if(dp[n]+bas>0) printf("%d\n",dp[n]);else printf("Impossible\n");return 0;
}

90pts code:

#include<bits/stdc++.h>
#define gc getchar()
#define inf -10000000
#define N 2000010
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn()
{
    int x=0,s=1,ch;while(((ch=gc)<'0'||ch>'9')&&ch!='-');
    if(ch^'-') x=ch^'0';else s=-1;
    while((ch=gc)>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');
    return s*x;
}
vector<int> v[N];int fp[N],rp[N],pfs[N],s[N],a[N],dp[N],cnt[N],bas;
inline int S(int l,int r) { return (r>=0?pfs[r]:0)-(l-1>=0?pfs[l-1]:0); }
inline int ins(vector<int> &v,int &fp,int &rp,int p) { while(fp<=rp&&dp[p]>=dp[v[rp]]) rp--;return v[++rp]=p,dp[v[fp]]; }
inline int del(vector<int> &v,int &fp,int &rp,int p) { if(fp<=rp&&v[fp]==p) fp++;return fp<=rp?dp[v[fp]]:inf; }
struct segment{ int l,r,v;segment *ch[2]; }*rt;
int build(segment* &rt,int l,int r)
{
    rt=new segment,rt->l=l,rt->r=r,rt->v=inf;
    if(l==r) return fp[l]=0,rp[r]=-1;int mid=(l+r)>>1;
    return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r);
}
inline int push_up(segment* &rt) { return rt->v=max(rt->ch[0]->v,rt->ch[1]->v); }
int ins(segment* &rt,int p,int x)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(l==r) return rt->v=ins(v[p],fp[p],rp[p],x);
    return ins(rt->ch[p>mid],p,x),push_up(rt);
}
int del(segment* &rt,int p,int x)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(l==r) return rt->v=del(v[p],fp[p],rp[p],x);
    return del(rt->ch[p>mid],p,x),push_up(rt);
}
int query(segment* &rt,int s,int t)
{
    int l=rt->l,r=rt->r,mid=(l+r)