【2023.11.13】NOIP2023模拟试题-33

T1

贪心地找到和最大的组的较大数删除是最优选择,因此开线段树维护全局最大数,并单点更新指定位置的值。

参考代码

展开代码
#include<bits/stdc++.h>
using namespace std;
#define fi(l,r) for(int i=l;i<=r;++i)
#define ff(i,l,r) for(int i=l;i<=r;++i)
#define ll long long
#define ly __int128
#define lp ll
#define lson(x) ((x)<<1)
#define rson(x) (lson(x)|1)
#define Lson(x) tr[lson(x)]
#define Rson(x) tr[rson(x)]
#define mod %P
#define N 500005
struct Info{
	int sum,pos;
	bool operator < (const Info b)const{return sum<b.sum;}
};
int n,m,tid,a[N],nxt[N],pre[N];
struct node{int l,r,sum,mxi;}tr[N*30];//全局查询+单点修改最大的 a[mxi]+a[nxt[mxi]]
#define valof(x) (a[x]+a[nxt[x]])
void pushup(int x){
	if(tr[x].l==tr[x].r)tr[x].mxi=tr[x].l;
	else{
		if(valof(Lson(x).mxi)>valof(Rson(x).mxi))tr[x].mxi=Lson(x).mxi;
		else tr[x].mxi=Rson(x).mxi;
	}
	return;
}
void build(int x,int l,int r){
	tr[x].l=l,tr[x].r=r;
	if(l==r){
		tr[x].sum=a[l],tr[x].mxi=l;
		return;
	}
	int mid=(l+r)>>1;
	build(lson(x),l,mid);
	build(rson(x),mid+1,r);
	pushup(x);
	return;
}
void update(int x,int pos){
	if(tr[x].l==tr[x].r){
		tr[x].sum=a[pos];
		return;
	}
	if(pos<Rson(x).l)update(lson(x),pos);
	else update(rson(x),pos);
	pushup(x);
	return;
}
int main(){
	freopen("necklace.in","r",stdin);
	freopen("necklace.out","w",stdout);
	scanf("%d %d %d",&tid,&n,&m);
	fi(1,n){
		nxt[i]=i+1;pre[i]=i-1;
		scanf("%d",&a[i]);
	}
	nxt[n]=1,pre[1]=n;
	build(1,1,n);
	ff(t,1,n-m){
		int x=tr[1].mxi;
		if(a[x]<a[nxt[x]])x=nxt[x];//找到和最大的一对里面值最大的一个
		nxt[pre[x]]=nxt[x];
		pre[nxt[x]]=pre[x];
		a[x]=0;//删除这个点
		update(1,x);
		update(1,pre[x]);
	}
	printf("%d",valof(tr[1].mxi));
	return 0;
}

T2

                     又是计数 dp 题

我们先考虑问题的弱化版:儿子的个数在 [li,ri] 之间,并且儿子序号的最ki>i

如果是这样的话,我们只需要开一个二维 dp f[i][j] 代表 第 i 到第 n 个点组成了 j 棵树的的组合方式。那么我们从 n1 的每个节点的加入其实就是从子树往根节点构建树,也就是新增一条链或者给子树当根节点的过程(因为倒序,所以一定是根节点)。那么就有

l[li,ri]{f[i1][j+1]+=f[i][j],l=0( 新增链 )f[i1][j]+=f[i][j],l=1( 新增一颗子树根节点 )f[i1][j1]+=f[i][j],l=2( 新增两个子树的共同根节点 )

为什么要从子树往根构建而不从根往子树构建呢?

因为考虑所有子树,我们只需要枚举根节点,

而若考虑所有根节点,我们就需要枚举子树。

显然枚举根节点更方便。

接下来我们考虑题目的限制条件。题目更改的条件是

如果第 i 个节点不为叶子,那么其儿子的最大编号 ki>i

其实就是从子树往根构建的过程中,除了新建子树以外,还可以添加到子树的非根节点上作为子树的子树。但是我们要从 1n 顺序枚举,所以不用考虑成为根节点的情况(不然不能保证子节点与父节点的大小关系)

因此,我们只需要新记录一维需要填补的空缺儿子位置个数 k 就可以了。当我们从 1n 顺序枚举的时候,显然,待定节点的序号一定大于父亲节点。对于 l[li,ri],我们分类讨论,只需要保证 l=12 的节点要有待定子节点就行了 :

l=0

显然只能当做新子树或者子树的子树。

因此:

f[i+1][j+1][k]+=f[i][j][k] ( 新建根节点 )

1

f[i+1][j][k1]+=f[i][j][k]×k ( 填补 k 个待定位中的任意一个 )

2

l=1

显然可能新增左或右 2 种待定节点。

因此:

f[i+1][j][k]+=f[i][j][k]×2 ( 根节点 + 创建左或右待定子节点 )

3

f[i+1][j][k1]+=f[i][j][k]×k×2 ( 填补待定位 + 创建待定子节点 )

4

l=2

显然可能会新增 1 种待定节点。

f[i+1][j+1][k+2]+=f[i][j][k] ( 根节点 + 创建待定子节点 )

5

f[i+1][j][k+1]+=f[i][j][k]×k ( 填补根节点 + 创建待定子节点 )

6

f[i+1][j1][k]+=f[i][j][k]×(j1)×k×2 ( 填补待定点 + 引入(所填补的待定点所在链以外)任意一条不保证序号大于自己的链放于左或右儿子 + 创建待定点 )

7

f[i+1][j][k+1]+=f[i][j][k]×j×2 ( 引入任意链 + 创建待定点 )

8

参考代码

/*
  bug:1.还要钦定这个节点下方待补节点的左右位置
 */
#include<bits/stdc++.h>
using namespace std;
#define fi(l,r) for(int i=l;i<=r;++i)
#define ff(i,l,r) for(int i=l;i<=r;++i)
#define ll long long
#define ly __int128
#define lp ll
#define lson(x) ((x)<<1)
#define rson(x) (lson(x)|1)
#define Lson(x) tr[lson(x)]
#define Rson(x) tr[rson(x)]
#define mod %P
#define P 1000000007
#define N 305
int tid,T,n,ql[N],qr[N];
ll f[N][N][N<<1];
void work(){
	memset(f,0,sizeof f);
	scanf("%d",&n);
	fi(1,n)scanf("%d %d",&ql[i],&qr[i]);
	f[0][0][0]=1;											//别忘了初始化
	fi(0,n-1){
		ff(j,0,i){
			ff(k,0,i<<1){
				if(f[i][j][k]==0)continue;
				f[i][j][k]%=P;
				if(ql[i+1]==0){
					f[i+1][j+1][k]+=f[i][j][k];				//新建根节点
					if(k)f[i+1][j][k-1]+=f[i][j][k]*k;		//填补k个待定位中的任意一个
				}
				if(ql[i+1]<=1&&qr[i+1]>=1){
					f[i+1][j+1][k+1]+=f[i][j][k]*2;			//根节点 + 创建左或右待定子节点	//bug #1 : 同时还要钦定这个节点下的待补节点位置
					f[i+1][j][k]+=f[i][j][k]*k*2;			//填补待定位 + 创建待定子节点
				}
				if(qr[i+1]==2){
					f[i+1][j+1][k+2]+=f[i][j][k];			//根节点+创建待定子节点
					f[i+1][j][k+1]+=f[i][j][k]*k;			//填补根节点+创建待定子节点
					if(j)f[i+1][j-1][k]+=f[i][j][k]*(j-1)*k*2;//填补待定点 + 引入(所填补的待定点所在链以外)任意一条不保证序号大于自己的链放于左或右儿子 + 创建待定点
					f[i+1][j][k+1]+=f[i][j][k]*j*2;			//引入任意链 + 创建待定点
				}
			}
		}
	}
	printf("%lld\n",f[n][1][0]%P);							//点统毕,只单链,无待定点
	return;
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d %d",&tid,&T);
	while(T-->0)work();
	return 0;
}

T3 T4

什么?T3 T4 是我能到达的地方吗?

posted @   DZhearMins  阅读(14)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示