洛谷P5979 [PA2014]Druzyny

(pre_i) 为只考虑 (d) 的限制下能转移到 (i) 的最小端点,发现 (pre_i) 随着 (i) 单调变化,因此可以通过单调队列求出 (pre_i)

然后就只用考虑 (c) 的限制了,考虑分治,对于区间 ([l,r]),设 (c_k) 为该区间的最大值,那么先递归 ([l,k-1]),然后处理 ([l,k-1])([k,r]) 的转移,最后递归 ([k,r])

其中处理转移时,(c) 的限制都一样,进行分类讨论得:

(pre_i geqslant k) 时,无法转移。

(l leqslant pre_i < k),在线段树上区间查询即可。

(pre_i < l and i-c_k < k-1) 时,每次 (i) 变化时,能转移的位置会相应的变化,每次 (O(1)) 维护即可。

(pre_i<l and i-c_k geqslant k-1) 时,整个左区间都能转移,二分出满足 (pre_j leqslant l) 最大的 (j) 后线段树上区间修改即可。

得总复杂度为 (T(n)=T(n-x)+T(x)+min(n-x,x)+log n=O(nlog n))

#include<bits/stdc++.h>
#define maxn 1000010
#define maxm 4000010
#define p 1000000007
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
#define mk make_pair
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,root=1,h=1,t,now=1;
int c[maxn],d[maxn],q[maxn],pre[maxn],pos[maxm];
pair<int,int> f[maxn],mx[maxm],tag[maxm];
int mod(int x)
{
    return x>=p?x-p:x;
}
pair<int,int> operator + (const pair<int,int> &a,const pair<int,int> &b)
{
    if(!b.second) return a;
    if(a.first==b.first) return mk(a.first,mod(a.second+b.second));
    return a.first>b.first?a:b;
}
pair<int,int> operator + (const pair<int,int> &a,const int &b)
{
    return mk(a.first+b,a.second);
}
void pushup(int cur)
{
    pos[cur]=c[pos[ls]]>=c[pos[rs]]?pos[ls]:pos[rs],mx[cur]=mx[ls]+mx[rs];
}
void pushtag(int cur,pair<int,int> &v)
{
    mx[cur]=mx[cur]+v,tag[cur]=tag[cur]+v;
}
void pushdown(int cur)
{
    pushtag(ls,tag[cur]),pushtag(rs,tag[cur]),tag[cur]=mk(0,0);
}
void build(int l,int r,int cur)
{
    if(l==r)
    {
        pos[cur]=l;
        return;
    }
    mx[cur]=tag[cur]=mk(0,0);
    build(l,mid,ls),build(mid+1,r,rs),pushup(cur);
}
void update(int L,int R,int l,int r,pair<int,int> &v,int cur)
{
    if(L<=l&&R>=r)
    {
        pushtag(cur,v);
        return;
    }
    pushdown(cur);
    if(L<=mid) update(L,R,l,mid,v,ls);
    if(R>mid) update(L,R,mid+1,r,v,rs);
    pushup(cur);
}
pair<int,int> query(int L,int R,int l,int r,int cur)
{
    if(L>R) return mk(0,0);
    if(L<=l&&R>=r) return mx[cur];
    pushdown(cur);
    if(R<=mid) return query(L,R,l,mid,ls);
    if(L>mid) return query(L,R,mid+1,r,rs);
    return query(L,R,l,mid,ls)+query(L,R,mid+1,r,rs);
}
int ask(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return pos[cur];
    if(R<=mid) return ask(L,R,l,mid,ls);
    if(L>mid) return ask(L,R,mid+1,r,rs);
    int p1=ask(L,R,l,mid,ls),p2=ask(L,R,mid+1,r,rs);
    return c[p1]>=c[p2]?p1:p2;
}
int find(int l,int r,int k)
{
    int pos;
    while(l<=r)
    {
        if(pre[mid]<=k) pos=mid,l=mid+1;
        else r=mid-1;
    }
    return pos;
}
void solve(int l,int r)
{
    if(l==r)
    {
        update(l,l,0,n,f[l],root);
        f[l]=query(l,l,0,n,root);
        return;
    }
    int k=ask(l+1,r,0,n,root);
    solve(l,k-1);
    pair<int,int> val=query(max(pre[k],l),max(k-c[k],l),0,n,root)+1;
    for(int i=max(k,l+c[k]);i<=r;++i)
    {
        if(pre[i]>=k) break;
        if(pre[i]>=l) f[i]=f[i]+(query(pre[i],min(i-c[k],k-1),0,n,root)+1);
        else if(i<k-1+c[k]) f[i]=f[i]+val,val=val+(f[i-c[k]+1]+1);
        else
        {
            int pos=find(i,r,l);
            update(i,pos,0,n,val,root),i=pos;
        }
    }
    solve(k,r);
}
int main()
{
    read(n);
    for(int i=1;i<=n;++i) read(c[i]),read(d[i]);
    for(int i=1;i<=n;++i)
    {
        while(h<=t&&d[i]<=d[q[t]]) t--;
        q[++t]=i;
        while(h<=t&&i-now+1>d[q[h]]) h+=q[h]==now,now++;
        pre[i]=now-1;
    }
    f[0]=mk(0,1),build(0,n,root),solve(0,n);
    if(f[n].first) printf("%d %d",f[n].first,f[n].second);
    else puts("NIE");
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/14243802.html