20220721

洗心革面,重新做人

image


轻松的音乐

类似题: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;
}
posted @ 2022-07-29 11:33  401rk8  阅读(60)  评论(1编辑  收藏  举报