1 条题解
-
0
题目大意
有 ()个池子,第 个池子每秒积攒 点法力(在第 秒时,所有池子都是 点法力)。
另外还有 条有向边,第 条有向边 表示从第 个池子到第 个池子需要 秒。
接下来给出 组询问,每组询问由 和 组成,询问一个人从任意池子开始,在第 秒结束时在第 个池子处,期间能收集最多多少点法力。
分析
首先我们会意识到一个问题,这个人可能会走来走去的,以至于一个池子经过特别多次,这种情况过于繁琐,我们是不想看到的,所以我们考虑简化问题为:只记录每个池子最后一次经过的时间。这样正确性是显然的。
但是这样不一定能恰好用完 秒,那么贪心地想,固定经过池子的顺序后,显然到达每个池子的时间越晚越好。另外,两个池子之间走最短路也是最优的。
具体地,假设 , 表示依次经过的 个池子,设 表示池子 到池子 的最短时间,则根据贪心策略:最后离开池子 的时间 最好为 。
故当固定 数组和 后,答案为:。但由于 数组和 都是未知的,且 是作为询问给出的,不好处理,所以考虑先将 分离出来,将式子改写:
$\begin{aligned} 原式 &=\sum_{i=1}^{k} (s-\sum_{j=i}^{k-1} dist(p_j,p_{j+1}))m_{p_i}\\ &= s\sum_{i=1}^km_{p_i}-\sum_{i=1}^k(m_{p_i}\sum_{j=i}^{k-1}dist(p_j,p_{j+1}))\\ \end{aligned}$
这样的话, 就只和 数组中元素所构成的集合有关了,且对于每个集合,式子后半部分的最小值是可以通过 dp 求解的。
于是,我们设
再设 表示目前走到 ,走过的集合为 ,式子后半部分的最小值。
转移时枚举上一步从哪个点走来 即可:
(记 表示 除掉元素 后的集合)
$f_{i,S}=\min\limits_{j \ne i,j \in S}(f_{j,S'}+dist(j,i)sum_{S'})$
可以在 时间内处理 。
处理完后会发现对于每一组询问 ,,我们要算的是下式:
直接算是 的,考虑优化。
套路性地按 分类后,对于每一个集合 构造一条斜率为 ,截距为 的直线。并将每一个询问的 看作变化的 ,每次询问所有直线在 处的最大值。李超线段树维护即可。时间复杂度
注意:①最好将所有的 先离散化,不然空间容易炸;②注意 INF 的值不能设的太小。
代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=18,MaxN=1e9,MaxS=1<<18,Q=2e5,INF=1e18; int n,m,q; int a[N+5]; int G[N+5] [N+5]; int sum[(1<<N)+5],f[N+5] [MaxS+5]; int s[Q+5],e[Q+5],val[Q+5]; struct LC_Tree{ int tot; int tag[MaxS<<2]; struct line{ int k,b; }p[MaxS+5]; void make_line(int _k,int _b){ ++tot; p[tot].k=_k,p[tot].b=_b; } int Calc(int id,int pos){ return p[id].k*pos+p[id].b; } void update(int now,int L,int R,int id){ int orig=tag[now]; int mid=L+R>>1; int lson=now<<1,rson=(now<<1)|1; if(Calc(id,val[mid])>Calc(orig,val[mid])) swap(id,orig); tag[now]=orig; if(L==R) return ; if(Calc(id,val[L])>Calc(orig,val[L])) update(lson,L,mid,id); if(Calc(id,val[R])>Calc(orig,val[R])) update(rson,mid+1,R,id); } int query(int now,int L,int R,int pos){ if(L==pos&&pos==R) return Calc(tag[now],val[pos]); int mid=L+R>>1; int lson=now<<1,rson=(now<<1)|1; if(pos<=mid) return max(Calc(tag[now],val[pos]),query(lson,L,mid,pos)); return max(Calc(tag[now],val[pos]),query(rson,mid+1,R,pos)); } }tr[N+5]; void initG(){ for(int i=1;i<=n;++i) for(int j=1;j<=n;++j){ if(i==j) G[i] [j]=0; else G[i] [j]=INF; } } void floyd(){ for(int k=1;k<=n;++k) for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) G[i] [j]=min(G[i] [j],G[i] [k]+G[k] [j]); } void work_f(){ for(int S=1;S<=(1<<n)-1;++S) for(int i=1;i<=n;++i) if(S&(1<<n-i)) sum[S]+=a[i]; for(int S=1;S<=(1<<n)-1;++S) for(int i=1;i<=n;++i){ f[i] [S]=INF; if(S&(1<<n-i)){ int tS=S^(1<<n-i); if(tS==0){ f[i] [S]=0; continue; } for(int j=1;j<=n;++j) if(tS&(1<<n-j)) if(G[j] [i]!=INF) f[i] [S]=min(f[i] [S],f[j] [tS]+G[j] [i]*sum[tS]); } } } void prepare_line(){ for(int i=1;i<=n;++i){ tr[i].tot=-1; tr[i].make_line(0,-INF); for(int S=1;S<=(1<<n)-1;++S) if(f[i] [S]<INF){ tr[i].make_line(sum[S],-f[i] [S]); tr[i].update(1,1,q,tr[i].tot); } } } signed main(){ //freopen("monster.in","r",stdin); //freopen("monster.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>n>>m; for(int i=1;i<=n;++i) cin>>a[i]; initG(); for(int i=1;i<=m;++i){ int u,v,w; cin>>u>>v>>w; G[u] [v]=w; } floyd(); work_f(); cin>>q; for(int i=1;i<=q;++i){ cin>>s[i]>>e[i]; val[i]=s[i]; } sort(val+1,val+1+q); prepare_line(); for(int i=1;i<=q;++i){ s[i]=lower_bound(val+1,val+1+q,s[i])-val; cout<<tr[e[i]].query(1,1,q,s[i])<<'\n'; } return 0; }
- 1
信息
- ID
- 7675
- 时间
- 5000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- 递交数
- 33
- 已通过
- 6
- 上传者