wcy‘s 比赛

T1,模因污染

原题链接:https://www.luogu.com.cn/problem/P8059

考试写了一个搜索(思路错误),写了一个搜索+并查集(思路接近了),结果答案可以用搜索或并查集,蚌。

看完题解后又想了一下,雀氏只用并查集或只用搜索会简单一些,由于考场思路是正着想的,结果要用到并查集撤销,没学过,只能反着想想,这道题要删边,感性理解雀氏反正想会好些。

所以考虑倒推,问题转换为:通过不断加边,则每个点的掉落时间为它第一次与 1 号节点联通的时间,可用并查集维护。

然后用链表更新与 1 号节点所在的连通块相连的连通块的内部节点:
pip_ipi 记录 iii 的父节点,初始值为 i。hhihh_ihhittitt_itti 记录 iii 节点所在连通块中编号最小和最大的元素,可以理解为队头队尾。

若要与 1 合并,还要先更新答案,而且每次合并时都是由大合并到小的,要保证 1 节点一直为根。

代码核心,并查集合并:

int find(int x){
	if(fa[x]!=x){
		p[x]=find(p[x]);
	}
	return p[x];
}

void merge(int a,int b,int c){
	int pa=find(a),pb=find(b);
	if(pa==pb) return;
	if(pa>pb) swap(pa,pb);
	if(pa==1 && c!=-1){
		for(int i=h[pb];i;i=ne[i]){
			ans[i]=c;
		}
	}
	fa[pb]=pa;
	ne[t[pa]]=h[pb];
	t[pa]=t[pb];
}

T2,饥饿的lpb

原题链接:https://www.luogu.com.cn/problem/P4801

岂可修,本来要作为下一次比赛的候选题,结果被抢先一步,白瞎了那 800 字了。

可以说这道题做过 3 次了,第二轮比赛前写和珅专题一次,找题时又一次,比赛时又一次,厄厄。

很显然的贪心题,但是有一个坑,这也是我第一次写时踩过的:

在喝了一口水后,狐狸开始吔饭了,所以一开始狐狸的体温是水的温度。

贪心思路就是我们可以每次选与此相差最大的饼干或水,但是有个小问题,最后一个饼干它的美味值不算。所以我们需要求出最大值开始的美味值与最小值开始的美味值,再对这两个值 maxmaxmax 一下就是最大值。

最小值很简单,排好序后依次吃下饼干就可以,最后的值为:
max(∣0,T[1]−W∣)+max(∣0,T[n]−W∣)max(| 0 , T[1]-W |)+max(| 0, T[n]-W|)max(∣0,T[1]W)+max(∣0,T[n]W)

代码也就明了了:

//求从最大值和最小值开始时的美味值
**int l=1,r=n;
	for(int i=1;i<=n;i++){
		if(i&1){
			cnt1+=max(abs(t[l]-last),abs(t[l]-w));
			last=t[l++];
		}
		else{
			cnt1+=max(abs(t[r]-last),abs(t[r]-w));
			last=t[r--];
		}
	}
	
	l=1,r=n;
	for(int i=1;i<=n;i++){
		if((i&1)^1){
			cnt2+=max(abs(t[l]-last),abs(t[l]-w));
			last=t[l++];
		}
		else{
			cnt2+=max(abs(t[r]-last),abs(t[r]-w));
			last=t[r--];
		}
	}
	//求最大美味值和最小美味值
	ans2=max(cnt1,cnt2);
	ans1=max(0ll,w-t[1])+max(0ll,t[n]-w);**

T3,真理医生的难题

原题链接:https://www.luogu.com.cn/problem/P4145?contestId=210764

线段树的题,考试时直接写线段树,然后过了,板子再加些修改。

虽然是区间修改,但是考虑到是对区间内的点进行 sqrt 操作,显然无法使用 pushdown(下传)维护区间值,所以考虑单点修改。

但是单点修改会超时,就该考虑优化,数列中的数不超过 101210^{12}1012 最多会 sqrt 不到 40 次。所以在修改时会有提前变为 1 的数,这时我们只需要再维护一个区间最大值,只要最大值为 1,我们就不用修改这段区间,这样就可以过了这道题。

代码是单点修改,区间查询板子再加上维护区间最大值很好写,不贴代码了。

T4,汉尼拔的晚宴

原题链接:https://www.luogu.com.cn/problem/CF939F

又是一道看出 DP 但是没推出状态转移方程的一道 单调队列优化DP,看完题解发觉思考的状态表示的方向有些问题。

暴力(CF的题不知道多少分?)O(n2)O(n^2)O(n2)
状态表示:fi,jf_{i,j}fi,jiii 分钟当前煎的是正面,背面煎了 jjj 分钟的最小翻面次数。
状态转移:

  • fi,j=min(fi−1,j,fi−1,j−1+1)f_{i,j}=min(f_{i-1,j},f_{i-1,j-1}+1)fi,j=min(fi1,j,fi1,j1+1)iii 属于任意一个 [l,r][l,r][l,r] 的区间)
  • fi,j=fi−1,jf_{i,j}=f_{i-1,j}fi,j=fi1,jiii 不属于任何一个 [l,r][l,r][l,r] 的区间)

代码:

	for(int i=1;i<=2*n;i++){
		for(int j=1;j<=n;j++){
			f[i][j]=min(f[i-1][j],f[i-1][j-1]+1);
			f[i][j]=f[i-1][j];
		}
	}

优化:
不难发现,不在区间内的 iii 是可以跳过的,因为这些 iii 不能翻面,背面煎的时间不变。

因此,我们可以列出另一个状态转移方程:fi,jf_{i,j}fi,jiii 个区间到 rir_iri 的时间内,背面煎了 jjj 分钟的最小翻面次数,这样时间复杂度就由 O(n2)O(n^2)O(n2) 降到了 O(nk)O(nk)O(nk)

考虑状态转移,因为在一个区间内翻奇数次面后偶数次面,最后的背面的结果是一样的,再加上需要最优性,显然只考虑在区间内 只翻一次面 和 只翻两次面 即可:

只翻一次面:

  • 发现翻一次面后,原来的正面变背面,原来的背面变正面,状态改变。
  • 枚举在翻面前煎了 kkk(0≤k≤r−l)(0\leq k\leq r-l)(0krl) ,则 fi,j=min(fi−1,r−j−k+1)f_{i,j}=min(f_{i-1,r-j-k}+1)fi,j=min(fi1,rjk+1)
  • j+kj+kj+k 是上一轮背面(翻面后正面)被煎的秒数。由于 k≤r−k≤r−krl 因此 r−j−k≥l−jr−j−k≥l−jrjklj ,每次只取最小值,可以维护单调队列。

翻两次面:

  • 发现翻两次面后,原来的正面依然在正面,背面同理,状态不变。
  • 枚举在煎了 kkk 秒后又翻回去 (0≤k≤r−l)(0\leq k\leq r-l)(0krl) ,则 fi,j=min(fi−1,j−k+2)f_{i,j}=min(f_{i-1,j-k}+2)fi,j=min(fi1,jk+2)
  • 即 上一轮的背面到这一轮还是背面。由于 k≤r−lk≤r−lkrl 因此 j−k≥j+l−rj−k≥j+l−rjkj+lr ,每次只取最小值,也可以用单调队列维护。

又发现,随着 jjj 的增加 r−j−kr-j-krjk 递减, j−kj-kjk 递增所以要分两次维护状态转移。

代码:

h=1; t=0; //r-(j+k)
for(int j=r;j>=0;--j){ 
	while(h<=t&&q[h]<l-j) ++h;
	while(h<=t&&f[!(i&1)][q[t]]>f[!(i&1)][r-j]) --t;
	q[++t]=r-j; f[i&1][j]=min(f[i&1][j],f[!(i&1)][q[h]]+1); 
}
h=1; t=0; //j-k
for(int j=0,mj=min(n,r);j<=mj;++j){ 
	while(h<=t&&q[h]<j-r+l) ++h;
	while(h<=t&&f[!(i&1)][q[t]]>f[!(i&1)][j]) --t;
		q[++t]=j; f[i&1][j]=min(f[i&1][j],f[!(i&1)][q[h]]+2);
	}
}

完,比赛题很好,就是撞了(看的出来,拔叔真饿了,建议拔叔去 T2 )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值