洛谷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(n\log 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;
}