青蕈领主
Luogu
LOJ
UOJ
BZOJ
我们用(L_i)表示以(i)为右端点的极长连续区间。
可以发现极长连续区间之间要么包含要么相离,因此我们可以用一棵树形结构来表示,且这棵树的根就是(L_n)。
考虑从下往上dp计算出方案数,设(f_i)表示(L_i)子树的方案数。
对于点(u),我们有(f_u=g_{|son_u|}prodlimits_{vin son_u}f_v)。
其中(g_n)为满足(forall iin[1,n],L_i=1)的(n+1)阶排列的数量。
那么如果我们能够计算出所有的(g_n),就能够暴力递归求出(f)了。
考虑一个转化,(g_n)等于所有连续区间要么包含(n+1),要么长度为(1)的(n+1)阶排列个数。我们称这样的排列为合法排列。
这个结论的正确性可以考虑原排列的连续区间与其逆排列的连续区间是一一对应的。
接下来我们先给出(g_n)的递推式:
考虑将一个(n)阶排列中的数都加上一,然后插入(1)来得到(n+1)阶排列,显然这是不重不漏的。
分两种情况考虑:
(1.)原排列合法,那么我们只需要保证(1)不在(2)旁边就行了,方案数((n-1)g_{n-1})。
(2.)原排列不合法,此时原排列应该恰好有一个不经过(n),长度(>1)且不被(L_n)以外的其他极大连续区间包含的极大连续区间,否则插入一个(1)并不会使得该排列变得合法。
设该极大连续区间的长度为(l),值域范围为([x,x+l))。显然(lin[2,n-2],xin[2,n-l])。
然后考虑方案数,固定了(l)之后(x)有(n-l-1)种取值。因为该区间中没有(2),因此经过(1)的子区间都不是连续的,因此我们可以把(1)看作(l+1),其它元素看做(1,cdots,l),方案数就是(g_l)。然后这个区间与其它(n-l)个区间(实际上就是单个数)组合在一起的方案数就是(g_{n-l})。
我们可以递推式直接(O(n^2))求出(g),也可以用分治FFT优化。
#include<cctype>
#include<cstdio>
using i64=long long;
const int N=50007,P=998244353;
int a[N];i64 f[N];char ibuf[1<<25],*iS=ibuf;
int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
i64 solve(int l,int r)
{
if(r-a[r]+1!=l) return 0;
i64 ans=1;int k=0;
for(int i=r-1;i>=l;i-=a[i],++k) ans=ans*(i-a[i]+1>=l? solve(i-a[i]+1,i):0)%P;
return ans*f[k]%P;
}
int main()
{
fread(ibuf,1,1<<25,stdin);
int t=read(),n=read();f[0]=1,f[1]=2;
for(int i=2,j;i<n;++i)
{
__int128 now=0;
for(j=2;j+j<i;++j) now+=f[j]*f[i-j];
f[i]=((i-1)*f[i-1]+now%P*(i-2))%P;
if(j==i/2) (f[i]+=(j-1)*f[j]%P*f[j])%=P;
}
while(t--)
{
for(int i=1;i<=n;++i) a[i]=read();
printf("%lld
",solve(1,n));
}
}
混合果汁
Luogu
LOJ
UOJ
BZOJ
把果汁按美味度降序排序,以单价为下标插入主席树,记录每个节点的(sum)果汁升数和(val)果汁总价。
每次询问二分最小美味度,查询美味度大于等于(mid)的总体积为(L)的最低总价。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[20],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
LL read(){LL x=0;char ch=Get();while(ch>57||ch<48)ch=Get();while(ch>=48&&ch<=57)x=x*10+(ch^48),ch=Get();return x;}
void write(int x){int top=0;if(x<0)Put('-'),x=-x;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('
');}
}
using namespace IO;
#define N 100007
#define K 100000
struct node{int d,p,l;}a[N];
int operator<(node a,node b){return a.d<b.d;}
int root[N],ls[N<<5],rs[N<<5],cnt;
LL sum[N<<5],val[N<<5];
#define mid ((l+r)>>1)
void insert(int &p,int l,int r,int x,int s,LL v)
{
ls[++cnt]=ls[p],rs[cnt]=rs[p],val[cnt]=val[p]+v,sum[cnt]=sum[p]+s,p=cnt;
if(l==r) return ;
x<=mid? insert(ls[p],l,mid,x,s,v):insert(rs[p],mid+1,r,x,s,v);
}
LL query(int p,int l,int r,LL s)
{
if(l==r) return s*l;
return s<=sum[ls[p]]? query(ls[p],l,mid,s):(val[ls[p]]+query(rs[p],mid+1,r,s-sum[ls[p]]));
}
int solve()
{
LL g=read(),L=read();int l=0,r=K,ans=0;
if(L>g) return -1;
while(l<=r) (L<=sum[root[mid]]&&query(root[mid],0,K,L)<=g)? (ans=mid,l=mid+1):(r=mid-1);
return a[ans].d;
}
int main()
{
int n=read(),m=read(),i;
for(a[0].d=-1,i=1;i<=n;++i) a[i].d=read(),a[i].p=read(),a[i].l=read();
for(sort(a+1,a+n+1),i=n;i;--i) insert(root[i]=root[i+1],0,K,a[i].p,a[i].l,1ll*a[i].p*a[i].l);
root[0]=root[1];
while(m--) write(solve());
return Flush(),0;
}