2021.07.21【NOIP提高B组】模拟 总结

2021.07.21【NOIP提高B组】模拟 总结

第一题:直接模拟枚举 0 0 0 9 9 9

第二题:模板树链剖分。

第三题:首先对于一个点的两棵子树中至少有两个点作为叶子。那么对于两棵子树我们设 f x f_{x} fx表示以 x x x作为最后一个叶子的答案(注意这不是最终答案)。那么就可以去上一棵子树找一个点 y y y转移过来,每一次枚举这两个点的最近公共祖先,用前缀和优化一下,可以证明时间复杂度是 O ( n ) O(n) O(n)​。​

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int n,a[100005],sz[100005],fa[100005],rnk[100005],dfn[100005],tot;
int la[100005],to[100005],ne[100005],cnt;
int la2[100005],to2[100005],ne2[100005],cnt2;
int cur[100005],f[100005],s1=inf,s2=0;
int ri[100005],le[100005],maxri[100005],maxle[100005],len1,len2;
int pr[100005],sf[100005];
void add(int x,int y){
	++cnt;
	to[cnt]=y;
	ne[cnt]=la[x];
	la[x]=cnt;
}
void add2(int x,int y){
	++cnt2;
	to2[cnt2]=y;
	ne2[cnt2]=la2[x];
	la2[x]=cnt2;
}
void swap(int &x,int &y){
	int t=x;
	x=y;
	y=t;
}
int max(int x,int y){
	return x>y?x:y;
}
int min(int x,int y){
	return x<y?x:y;
}
void dfs(int x){
	dfn[x]=++tot;
	rnk[tot]=x;
	sz[x]=1;
	for (int i=la[x];i;i=ne[i]){
		int y=to[i];
		dfs(y);
		sz[x]+=sz[y];
	}
}
void prer(int x){
	ri[++len1]=x;
	if (sz[x]==1)return;
	prer(to2[la2[x]]);
}
void prel(int x){
	le[++len2]=x;
	if (sz[x]==1)return;
	prel(to[la[x]]);
}
void dfs2(int x){//ri:len1,le:len2
	for (int i1=la[x];i1;i1=ne[i1]){
		dfs2(to[i1]);
		len1=len2=0;
		int y=to[i1];
		int z=to[ne[i1]];
		if (!z)continue;
		prer(y);
		prel(z);
		maxri[0]=maxle[0]=a[x];
		for (int i=1;i<=len1;i++)maxri[i]=max(maxri[i-1],a[ri[i]]);
		for (int i=1;i<=len2;i++)maxle[i]=max(maxle[i-1],a[le[i]]);
		for (int i=len1-1;i>=0;i--)maxri[i+1]=maxri[i];
		for (int i=len2-1;i>=0;i--)maxle[i+1]=maxle[i];
		pr[0]=-inf;
		for (int i=1;i<=len1;i++)pr[i]=max(pr[i-1],f[ri[i]]);
		sf[len1+1]=-inf;
		for (int i=len1;i;i--)sf[i]=max(sf[i+1],f[ri[i]]-maxri[i]);
		int j=1;
		for (int i=1;i<=len2;i++){
			int ii=le[i];
			if (f[ii]!=0)continue;
			f[ii]=-inf;
			while (maxri[j]<maxle[i]&&j<=len1) j++;
			if (j<=len1)f[ii]=max(f[ii],sf[j]+a[ii]);
			f[ii]=max(f[ii],pr[j-1]-maxle[i]+a[ii]);
		}
	}
}
void pre(int x){
	f[x]=a[x];
	if (sz[x]==1)return;
	pre(to[la[x]]);
}
void qry(int x){
	s2=max(s2,f[x]);
	if (sz[x]==1)return;
	qry(to2[la2[x]]);
}
int main(){
	cin>>n;
	for (int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		int len;
		scanf("%d",&len);
		for (int j=1;j<=len;j++)scanf("%d",&cur[j]),fa[cur[j]]=i,add2(i,cur[j]);
		for (int j=len;j;j--)add(i,cur[j]);
	}
	dfs(1);
	pre(1);
	dfs2(1);
	qry(1);
	cout<<s2;
}

第四题:断环成链。设 f i f_i fi表示以 i i i​结尾的答案,那么只要在前面找前 3 3 3大的数即可(互不相同),起点枚举 5 5 5个数(互不相同)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值