『Solution of Monster&划艇&烟火表演』
ABC275F Monster
其实就是对凸壳的处理办法
显然建立
显然离开子树后子树都是整体操作的
有
显然可以优化为:
现在我们做到了
换个写法:
当合并时这个形式感觉由于
容易发现由于
首先假定成立,则设
相当于把
那么在原本的斜率递增的情况下会删除一个尾巴/脑袋,还是递增的。
而且至多有
考虑用数据结构维护斜率,然后每次删除增加,我们需要启发式合并?。
不妨维护
讲讲实现(谁再喷我水):
维护
然后维护
(可以看作是三个凸包,
然后我们将斜率小于零的部分以及初始位置小于
接着我们插入
注意到我们只需要维护这些二元组的快速合并和查首项,用可并堆(左偏树)即可。
#include<bits/stdc++.h>
using namespace std;
#define N 105050
#define int long long
namespace heap{
int cnt,lc[N<<2],rc[N<<2],dep[N<<2],px[N<<2],dk[N<<2];
int new_node(int x,int k){
px[++cnt]=x;dk[cnt]=k;return cnt;
}
int merge(int a,int b){
/*
维护一个px的左偏树小根堆方便删
*/
if(!a||!b)return a^b;
if(px[a]>px[b]||(px[a]==px[b]&&dk[a]>dk[b]))swap(a,b);
rc[a]=merge(rc[a],b);
if(dep[lc[a]]<dep[rc[a]])swap(lc[a],lc[b]);
dep[a]=dep[rc[a]]+1;return a;
}
}
int lc[N],rc[N],a[N],b[N],sta[N],top,rt[N],f0[N],n,m;
void sol(int x){
if(!x)return ;
sol(lc[x]);sol(rc[x]);
f0[x]=f0[lc[x]]+f0[rc[x]];
rt[x]=heap::merge(rt[lc[x]],heap::merge(rt[rc[x]],heap::new_node(0,b[x])));
//f[x,i]=min_{j>=i}(f[lc,j]+f[rc,j]+(j-i)*b[x])=min(f[lc,j]+f[rc,j]+j*b[x])-b[x]*i
/*
初始化这棵树是(a[x],0) & (0,b[x])
也即f[x,a[x]]之后的全0,前面的每个增量是b[x]
*/
int k=0;
while(k+heap::dk[rt[x]]<0||heap::px[rt[x]]<a[x]){
k+=heap::dk[rt[x]];int tmp=heap::px[rt[x]];
rt[x]=heap::merge(heap::lc[rt[x]],heap::rc[rt[x]]);
f0[x]+=(heap::px[rt[x]]-tmp)*k;
}
rt[x]=heap::merge(rt[x],heap::merge(heap::new_node(heap::px[rt[x]],k),heap::new_node(0,-b[x])));
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<=n;++i)cin>>b[i];
int rot=0;
for(int i=1;i<=n;i++){
int k=top;
while(k&&b[sta[k]]<b[i])--k;
if(k)rc[sta[k]]=i;
if(k<top)lc[i]=sta[k+1];
sta[++k]=i,top=k;
}
int mx=-0x3f3f3f3f,id=n;
for(int i=1;i<=n;i++)if(b[i]>mx)mx=b[i],id=i;
sol(id);
cout<<f0[id]<<"\n";
}
APIO2016 划艇
题意就是给定若干
要求对于其所有子序列而言,子序列每个数只能出现
显然有一个
一个有点像二维前缀和的东西。
可以修改定义为
注意到第二维很大,有
感觉最开始那个二位前缀和还有优化空间,不妨设
这是
还是很那个。我们的核心问题在于减少第二维的状态。
考虑修改状态为:将
诚然这很难以转移,但是我们不妨一整个转移,略去
我们对第二维做一个前缀和好像就做完了。
对于转移系数可以
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1e9+7;
#define N 1505
int b[N],f[N][N<<1],s[N][N<<1],trans[N][N<<1],n,m,l[N],r[N],cnt[N][N<<1],zhs[N<<1][N],jc[N],inv[N];
int power(int a,int b){
int ans=1;
while(b){
if(b&1)ans=ans*a%p;a=a*a%p;b>>=1;
}
return ans;
}
int C(int n,int m){
if(n<m)return 0;
if(n-m<m)m=n-m;int res=1;
for(int i=n;i>n-m;--i)res=res*i%p;
res=res*inv[m]%p;return res;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=jc[i-1]*i%p;inv[n]=power(jc[n],p-2);
for(int i=n;i;--i)inv[i-1]=inv[i]*i%p;
for(int i=1;i<=n;++i)cin>>l[i]>>r[i],b[++m]=l[i],++r[i],b[++m]=r[i];
sort(b+1,b+m+1);m=unique(b+1,b+m+1)-b-1;
f[0][0]=s[0][0]=1;
for(int i=1;i<=m;++i)s[0][i]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<m;++j)cnt[i][j]=cnt[i-1][j];
for(int j=1;j<m;++j)if(l[i]<=b[j]&&b[j+1]<=r[i])cnt[i][j]++;
}
for(int j=1;j<m;++j){
for(int i=0;i<=cnt[n][j];++i){
zhs[j][i]=C(b[j+1]-b[j],i);
}
for(int i=1;i<=cnt[n][j];++i){
for(int x=0;x<i;++x)(trans[i][j]+=zhs[j][x+1]*jc[i-1]%p*inv[x]%p*inv[i-1-x]%p)%=p;
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<m;++j){
if(l[i]<=b[j]&&b[j+1]<=r[i]){
for(int k=0;k<i;++k){
int len=b[j+1]-b[j],c=cnt[i][j]-cnt[k][j];
f[i][j]+=s[k][j-1]*trans[c][j]%p;
}
}
f[i][j]%=p;
s[i][j]=(f[i][j]+s[i][j-1])%p;
}
}
int res=0;res=(res%p+p)%p;
for(int i=1;i<=n;++i)res=(res+s[i][m-1])%p;
res=(res%p+p)%p;
cout<<res<<"\n";
}
/*
3
1 5 3 9 2 7
*/
Apio2016 烟火表演
弄出monster之后这题我谔谔
容易写出朴素DP式,也就是
发现绝对值函数是凸的,所以叶子的
然后我们考虑一个绝对值函数有什么影响。
由于我们关心最小值,所以我们只关心斜率为
不难发现假设原本段是
我们只关心最小值,所以我们只需要维护半个凸壳,也就是
那么我们可以忽略
其实是简单的,有
(我们并没有管它,但是它合并上来后会形成拐点)
发现我们只需要维护凸壳上点的横坐标,维护即可。
也即使用一个可并堆(大根堆删后面),依次合并儿子,然后弹出前面的
这里注意对于叶子节点而言,即使这是一个点也要看成一段,这是
#include<bits/stdc++.h>
using namespace std;
#define N 656500
#define int long long
int lc[N<<1],rc[N<<1],x[N<<1],d[N<<1],cnt;
namespace heap{
int new_node(int v){
++cnt;x[cnt]=v;return cnt;
}
int merge(int a,int b){
if(!a||!b)return a^b;
if(x[a]<x[b])swap(a,b);
rc[a]=merge(rc[a],b);
if(d[lc[a]]<d[rc[a]])swap(lc[a],rc[a]);
d[a]=d[rc[a]]+1;
return a;
}
}
vector<int>e[N];
int rt[N],dis[N],n,m,ans,fa[N];
void dfs(int u){
if(u>n){rt[u]=heap::merge(heap::new_node(dis[u]),heap::new_node(dis[u]));return ;}
for(auto v:e[u]){
dfs(v);rt[u]=heap::merge(rt[u],rt[v]);
}
for(int i=e[u].size()-1;i;--i)rt[u]=heap::merge(lc[rt[u]],rc[rt[u]]);
if(u==1){
rt[u]=heap::merge(lc[rt[u]],rc[rt[u]]);
while(rt[u])ans-=x[rt[u]],rt[u]=heap::merge(lc[rt[u]],rc[rt[u]]);return ;
}
int posr=x[rt[u]];rt[u]=heap::merge(lc[rt[u]],rc[rt[u]]);
int posl=x[rt[u]];rt[u]=heap::merge(lc[rt[u]],rc[rt[u]]);
rt[u]=heap::merge(rt[u],heap::merge(heap::new_node(posl+dis[u]),heap::new_node(posr+dis[u])));
}
signed main(){
cin>>n>>m;
for(int i=2,f;i<=n+m;++i)cin>>f>>dis[i],e[f].push_back(i),ans+=dis[i],fa[i]=f;
dfs(1);
cout<<ans<<"\n";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!