20220721
洗心革面,重新做人
轻松的音乐
类似题:CF1476F Lanterns
关键结论是一样的:如果当前覆盖情况是 \([1,l),(r,?](l\le r)\),那么状态中只需要记录 \(l\)
原因是之后一定会有一个点 \(i>r\) 向左覆盖 \(l\),那么 \([l,r]\) 一定都会向右,在 \(i\) 处考虑 \([l,r]\) 向右能覆盖的区域即可
然后就可以 DP 了:设 \(f[i,j]\) 表示前 \(i\) 个位置至少覆盖了前缀 \(j\) 的最小代价,转移:
- 继承:\(f[i,j]\leftarrow f[i-1,j]\)
- \(i\) 向右:\(f[i,i+p_{i}]\leftarrow f[i-1,i]\)
- \(i\) 向左:枚举位置 \(j\),让 \((j,i)\) 都向右,\(f[i,\max(i-1,\max_{k\in(j,i)}\{k+p_{k}\})]\leftarrow f[j,i-p_{i}-1]\)
- 付出代价覆盖:\(f[i,j]\leftarrow f[i,j-1]+a_{j}\)(注意这里不是从 \(f[i-1,j-1]\) 转移)
- 最后做一个后缀 \(\min\)
显然有效状态只有 \([i-p,i+p]\),时空复杂度 \(O(np)\)
code
const int N = 5e4+5;
int n,a[N],b[N],*f[N],Buf[N*64],*buf=Buf;
signed main() { freopen("kon.in","r",stdin); freopen("kon.out","w",stdout);
io>>n; For(i,1,n) io>>a[i]; For(i,1,n) io>>b[i];
For(i,0,n) {
buf += 32, f[i] = buf, buf += 32;
For(j,-30,30) f[i][j] = inf;
}
For(i,-30,0) f[0][i] = 0;
For(i,1,n) {
For(j,-30,29) ckmin(f[i][j],f[i-1][j+1]);
ckmin(f[i][min(n,i+a[i])-i],f[i-1][1]);
rFor(j,i-1,p, k = i-1, p = max(0,i-a[i]-1))
ckmin(f[i][k-i],f[j][p-j]), ckmax(k,j+a[j]);
For(j,max(-29,1-i),min(30,n-i)) ckmin(f[i][j],f[i][j-1]+b[i+j]);
rFor(j,29,-30) ckmin(f[i][j],f[i][j+1]);
}
io<<f[n][0];
return 0;
}
论文题
参见 2021 集训队论文《鞅与一类关于停时的概率与期望问题》
最小生成树
先考虑第二类边是链的情况。用线段树维护区间 \([l,r]\) 的答案,\(f[0/1,0/1]\) 表示 \(l,r\) 是否与 \(0\) 连通,合并时根据 \(mid,mid+1\) 与 \(0\) 的连通性决定是否需要 \((mid,mid+1)\) 间的第二类边即可
注意这个合并的过程不关注连通块的样子,只需要知道与 \(0\) 的连通性和 \((mid,mid+1)\) 的边权
尝试对一般情况找一条等效链。对第二类边单独做 kruskal,维护每个连通块的等效链,合并连通块时也合并等效链(首尾相接,边权为当前边的边权)
正确性在于如果原图中的边 \((u,v)\) 在等效链中连接了 \((x,y)\),那么对全图做 kruskal,考虑到 \((u,v)\) 时 \(u,x\) 和 \(v,y\) 一定连通
一个细节是叶子的 \(f[0,1]=f[1,0]=0\) 而不是 \(\inf\),因为有一边是 \(0\) 保证了 \(i\) 能与 \(0\) 连通,而有一边连通 \(i\) 的两边就全连通了(否则合并叶子时无法连成 \(0-i-i+1\))
code
const LL infl = 0x3f3f3f3f3f3f3f3f;
const int N = 3e5+5;
int n,m,q,ind,a[N],fa[N],head[N],suf[N],id[N],pos[N];
LL b[N];
tuple<int,int,int> e[N];
#define ls (u<<1)
#define rs (u<<1|1)
#define mid (l+r>>1)
struct Node {
LL w,s[2][2];
Node operator + (const Node &x) const {
Node res; res.w = x.w;
For(i,0,1) For(j,0,1) {
res.s[i][j] = s[i][1]+x.s[1][j];
if( w < infl )
ckmin(res.s[i][j], min(s[i][0]+x.s[1][j],s[i][1]+x.s[0][j])+w);
}
return res;
}
} t[N*4];
void bld(int u=1,int l=1,int r=n) {
if( l == r ) return t[u].w = b[id[l]], t[u].s[1][1] = a[id[l]], void();
bld(ls,l,mid), bld(rs,mid+1,r), t[u] = t[ls] + t[rs];
}
void mdf(int p,int x,int u=1,int l=1,int r=n) {
if( l == r ) return t[u].s[1][1] = x, void();
p<=mid ? mdf(p,x,ls,l,mid) : mdf(p,x,rs,mid+1,r), t[u] = t[ls] + t[rs];
}
int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); }
signed main() { freopen("mst.in","r",stdin); freopen("mst.out","w",stdout);
io>>n>>m; For(i,1,n) io>>a[i], fa[i] = head[i] = i;
For(i,1,m, x,y,z) io>>x>>y>>z, e[i] = {z,x,y}; sort(e+1,e+m+1);
For(i,1,m) {
int w,u,v; tie(w,u,v) = e[i];
if( (u=find(u)) == (v=find(v)) ) continue;
suf[u] = head[v], b[u] = w, fa[u] = v, head[v] = head[u];
}
For(i,1,n) b[find(i)] = infl;
For(i,1,n) if( !pos[i] )
for(int j = head[find(i)]; j; j = suf[j]) id[++ind] = j, pos[j] = ind;
ast(ind==n);
bld();
io>>q; while( q-- ) {
int u=read(); mdf(pos[u],read());
io<<t[1].s[1][1]<<endl;
}
return 0;
}