先二分答案,求出每个位置需要被覆盖多少次,那么从左往右扫,对于每个位置,如需被覆盖 x x x次,那就贪心的选择前 x x x个能覆盖到他且覆盖的最远的 x x x条线段。随便维护一下即可。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=200010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,K,a[Maxn];LL b[Maxn],A;
struct Line{int l,r;}p[Maxn];
struct Seg{int l,r,lc,rc,mx,tag;}tr[Maxn<<1];
int tot;
void Add(int x,int v){tr[x].mx+=v,tr[x].tag+=v;}
void up(int x)
{
int lc=tr[x].lc,rc=tr[x].rc;
tr[x].mx=max(tr[lc].mx,tr[rc].mx);
}
void down(int x)
{
int t=tr[x].tag;
if(t)tr[x].tag=0,Add(tr[x].lc,t),Add(tr[x].rc,t);
}
void build(int l,int r)
{
int x=++tot;
tr[x].l=l;tr[x].r=r;tr[x].tag=0;
if(l==r){tr[x].mx=b[l];return;}
int mid=l+r>>1;
tr[x].lc=tot+1,build(l,mid);
tr[x].rc=tot+1,build(mid+1,r);
up(x);
}
void add(int x,int l,int r,int v)
{
if(tr[x].l==l&&tr[x].r==r){Add(x,v);return;}
int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
down(x);
if(r<=mid)add(lc,l,r,v);
else if(l>mid)add(rc,l,r,v);
else add(lc,l,mid,v),add(rc,mid+1,r,v);
up(x);
}
int pos,v;
void go(int x)
{
// printf("%d\n",x);
if(tr[x].l==tr[x].r){pos=tr[x].l;v=tr[x].mx;return;}
int lc=tr[x].lc,rc=tr[x].rc;
down(x);
if(tr[lc].mx>0)go(lc);
else go(rc);
}
vector<int>h[Maxn];
bool check(LL o)
{
priority_queue<int>q;
for(int i=1;i<=n;i++)b[i]=max(0LL,(o-a[i]+A-1)/A);
tot=0;build(1,n);
int u=0,pp=0;
while(tr[1].mx&&u<K)
{
go(1);
while(pp<pos)
{
pp++;
for(int i=0;i<h[pp].size();i++)q.push(h[pp][i]);
}
if(u+v>K)return false;
u+=v;
while(v--)
{
if(q.empty())return false;
if(q.top()<pos)return false;
add(1,pos,q.top(),-1);q.pop();
}
}
return(!tr[1].mx&&u<=K);
}
int main()
{
int T=read();
while(T--)
{
n=read(),m=read(),K=read(),A=read();
int mn=inf;
for(int i=1;i<=n;i++)a[i]=read(),mn=min(mn,a[i]);
for(int i=1;i<=m;i++)
{
int l=read(),r=read();
h[l].push_back(r);
}
LL l=mn,r=mn+K*A;
while(l<=r)
{
LL mid=l+r>>1;
if(check(mid))l=mid+1;
else r=mid-1;
}
printf("%lld\n",l-1);
for(int i=1;i<=n;i++)
{
while(h[i].size())h[i].pop_back();
}
}
}
DP, f i , j , k , l f_{i,j,k,l} fi,j,k,l表示到第 i i i个位置,前驱为 j j j,后继为 k k k,当前值为 l l l的方案数,滚动数组优化一下空间,前缀和优化一下转移就可以了。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=55,Maxm=153;
const int inf=2147483647;
const int mod=998244353;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
void upd(int&x,int y){x+=y;if(x>=mod)x-=mod;}
int n,a[Maxn],f[2][Maxm][Maxm][Maxm],s1[2][Maxm][Maxm],s2[2][Maxm][Maxm],s3[2][Maxm];
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
int o=0,ans=0;
for(int i=1;i<=a[1];i++)f[0][0][151][i]=1;
for(int l=0;l<=151;l++)
for(int r=l+1;r<=151;r++)
{
s1[o][l][r]=0;
for(int p=r+1;p<=151;p++)upd(s1[o][l][r],f[o][l][p][r]);
}
for(int l=0;l<=151;l++)
for(int r=l+1;r<=151;r++)
{
s2[o][l][r]=0;
for(int p=l-1;p>=0;p--)upd(s2[o][l][r],f[o][p][r][l]);
}
for(int p=0;p<=151;p++)
{
s3[o][p]=0;
for(int l=0;l<=p;l++)
for(int r=p;r<=151;r++)
upd(s3[o][p],f[o][l][r][p]);
}
for(int i=2;i<=n;i++)
{
o^=1;
for(int l=0;l<=151;l++)
for(int r=l+1;r<=151;r++)
s1[o][l][r]=s2[o][l][r]=0;
for(int p=0;p<=151;p++)s3[o][p]=0;
for(int l=0;l<=151;l++)
for(int r=l;r<=151;r++)
{
if(l==r&&l>0&&l<=a[i])
{
f[o][l][l][l]=s3[o^1][l];
upd(s3[o][l],f[o][l][l][l]);
if(i==n)upd(ans,f[o][l][l][l]);
}
for(int p=max(1,l+1);p<=min(a[i],r-1);p++)
{
f[o][l][r][p]=(s1[o^1][l][r]+s2[o^1][l][r])%mod;
int t=f[o][l][r][p];
upd(s1[o][l][p],t);
upd(s2[o][p][r],t);
upd(s3[o][l],t),upd(s3[o][r],t),upd(s3[o][p],t);
if(i==n)upd(ans,t);
}
}
}
printf("%d",ans);
}
求出每个位置的前驱后继,如果没有的话就设为自己的位置,那么就是求有多少个区间 [ l , r ] [l,r] [l,r],满足 n e x t r next_r nextr为这段 n e x t next next的最大值, p r e l pre_l prel为这段 p r e pre pre的最小值,先用两个单调栈搞一搞就好了,本以为要线性,但是带了一个 l o g log log还是跑到了LOJ r a n k 2 rank2 rank2。
#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=300010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,a[Maxn],pre[Maxn],nxt[Maxn],sta[Maxn],top,pos[Maxn],L[Maxn];LL ans;
int main()
{
int T=read();
while(T--)
{
n=read();ans=0;
for(int i=1;i<=n;i++)pos[i]=pre[i]=nxt[i]=0;
for(int i=1;i<=n;i++)
{
a[i]=read();
if(pos[a[i]])pre[i]=pos[a[i]],nxt[pos[a[i]]]=i;
else pre[i]=i;
pos[a[i]]=i;
}
for(int i=1;i<=n;i++)if(!nxt[i])nxt[i]=i;
top=0;
for(int i=1;i<=n;i++)
{
while(top&&nxt[i]>=nxt[sta[top]])top--;
if(!top)L[i]=1;else L[i]=sta[top]+1;
sta[++top]=i;
}
top=0;
for(int i=1;i<=n;i++)
{
while(top&&pre[i]<pre[sta[top]])top--;
if(i==pre[i])sta[++top]=i;
if(i==nxt[i])
{
int l=1,r=top;
while(l<=r)
{
int mid=l+r>>1;
if(sta[mid]>=L[i])r=mid-1;
else l=mid+1;
}
r++;
ans+=(top-r+1);
}
}
printf("%lld\n",ans);
}
}