2019 百度之星复赛 补题

本文探讨了树形DP在解决区间选择问题上的应用,通过分析区间端点的影响,提出了有效的算法解决方案。同时,深入研究了一个数学问题,揭示了通过观察操作规律找到解题关键的方法。文章还涉及了一种特殊的二叉树结构构建问题,展示了如何通过递归和状态转移方程确定最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A - Diversity HDU - 6725 (树形dp)

若两个区间有交,显然可以把两个区间的值,都取在区间交集的端点的其中一个,

若没有交集的话,[l1,r1]<[l2,r2]时,取到r1和l2,也在区间的端点

因此,[l,r]的数,只由两端点决定,二选一,树形dp

#include<bits/stdc++.h>
using namespace std;
#define pb push_back 
typedef long long ll;
const int N=1e5+10;
int t,n,u,v;
vector<int>E[N];
ll dp[N][2],a[N][2];
void dfs(int u,int fa)
{
	for(int i=0;i<E[u].size();++i)
	{
		int v=E[u][i];
		if(v==fa)continue;
		dfs(v,u);
		for(int j=0;j<2;++j)
		dp[u][j]+=max(dp[v][1]+abs(a[u][j]-a[v][1]),dp[v][0]+abs(a[u][j]-a[v][0]));
	} 
} 
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		{
			E[i].clear();
			dp[i][0]=dp[i][1]=0;
		}
		for(int i=1;i<n;++i)
		{
			scanf("%d%d",&u,&v);
			E[u].pb(v),E[v].pb(u); 
		}
		for(int i=1;i<=n;++i)
		scanf("%lld%lld",&a[i][0],&a[i][1]);
		dfs(1,-1);
		printf("%lld\n",max(dp[1][0],dp[1][1]));
	}
	return 0;
}

 

 

B - Transformation HDU - 6726 (数学/找规律)

赛后补题,

第一次操作,(a,b+(b-a))或(a+(a-b),b),要么前项加(a-b),要么后项加(b-a)

第二次操作,要么前项加2*(a-b),要么后项加2*(b-a)

第三次操作,要么前项加4*(a-b),要么后项加4*(b-a)

所以,c-a必须是a-b的倍数,d-b必须是a-b的倍数

特判a=b的情形,再讨论x=(c-a)/(a-b)和y=(d-b)/(a-b),显然答案唯一,与字典序无关

x和y非负且不能有交集,且x与y的并集构成二进制形如000001111111111的序列

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
ll a,b,c,d,x,y;
bool ok(ll x)//判断x是否形如000111111 右起一段连续的1 
{
	x++;
	x-=x&-x;
	return x==0; 
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		//每次操作 必a+=k*(a-b) 或b+=k*(b-a) k为2的幂次且从1起不断*2 
		scanf("%lld%lld%lld%lld",&a,&b,&c,&d);//c-a=x*(a-b) d-b=y*(b-a) x和y二进制无交集 
		if(a==b)//c和d不会改变 
		{
			if(c==a&&d==b)printf("Yes\n\n");
			else puts("No");
			continue; 
		}
		if((c-a)%(a-b)||(d-b)%(b-a))
		{
			puts("No");
			continue;
		}
		x=(c-a)/(a-b);y=(d-b)/(b-a);
		if(x<0||y<0||(x&y)||!ok(x|y))
		{
			puts("No");
			continue;
		}
		puts("Yes");
		for(int i=0;(x>>i&1)|(y>>i&1);i++)
		{
			if(x>>i&1)putchar('B');
			else putchar('A');
		}
		puts("");
	}
	return 0;
}

C - Quasi Binary Search Tree HDU - 6727 (二叉树/构造)

赛中AC,纪念一下

对于一棵子树来讲,其值一定是连续的,

所以子树值一定会被分成三段,[l,x],x+1,[x+2,r]

op[u]=0代表这棵子树中值最小的值出现在左子树,

op[u]=1代表出现在右子树

op[u]=2代表出现在根,根的时候分左子和右子的数量来讨论

数量相等时,再分左子和右子的最小值哪个小来讨论

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#define fi first
#define se second
#define pb push_back
using namespace std;
const int INF=0x3f3f3f3f;
typedef long long ll;
const int mod=1e9+7;
const int N=1e5+10;
int t,n;
int rt,mn[N],op[N],sz[N],res[N];
int ls[N],rs[N],in[N];
int bs[N];
void init()
{
	bs[1]=233;
	for(int i=2;i<N;++i)
	bs[i]=(1ll*bs[i-1]*233)%mod;
}
void dfs(int u)
{
	sz[u]=1;
	if(ls[u])
	{
		dfs(ls[u]);
		sz[u]+=sz[ls[u]];
		if(mn[ls[u]]<mn[u])
		{
			mn[u]=mn[ls[u]];
			op[u]=0;
		}
	}
	if(rs[u])
	{
		dfs(rs[u]);
		sz[u]+=sz[rs[u]];
		if(mn[rs[u]]<mn[u])
		{
			mn[u]=mn[rs[u]];
			op[u]=1;
		}
	}
	if(u<mn[u])
	{
		mn[u]=u;
		op[u]=2;
	}
}
void dfs2(int u,int l,int r)
{
	if(!u||l>r)return;
	if(l==r)
	{
		res[u]=l;
		return;
	}
	if(op[u]==0)
	{
		res[u]=l+sz[ls[u]];
		dfs2(ls[u],l,l+sz[ls[u]]-1);
		dfs2(rs[u],l+sz[ls[u]]+1,r);
	}
	else if(op[u]==1)
	{
		res[u]=l+sz[rs[u]];
		dfs2(rs[u],l,l+sz[rs[u]]-1);
		dfs2(ls[u],l+sz[rs[u]]+1,r);
	}
	else 
	{
		if(l+sz[ls[u]]<l+sz[rs[u]])
		{
			res[u]=l+sz[ls[u]];
			dfs2(ls[u],l,l+sz[ls[u]]-1);
			dfs2(rs[u],l+sz[ls[u]]+1,r);
		} 
		else if(l+sz[ls[u]]>l+sz[rs[u]])
		{
			res[u]=l+sz[rs[u]];
			dfs2(rs[u],l,l+sz[rs[u]]-1);
			dfs2(ls[u],l+sz[rs[u]]+1,r);
		}
		else
		{
			res[u]=l+sz[ls[u]];
			if(mn[ls[u]]<mn[rs[u]])
			{
				dfs2(ls[u],l,l+sz[ls[u]]-1);
				dfs2(rs[u],l+sz[ls[u]]+1,r);
			}
			else if(mn[ls[u]]>mn[rs[u]])
			{
				dfs2(rs[u],l,l+sz[rs[u]]-1);
				dfs2(ls[u],l+sz[rs[u]]+1,r);
			}
		}
	}
}
int main()
{
	init();
	mn[0]=INF;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		in[i]=ls[i]=rs[i]=sz[i]=0,mn[i]=n+1;
		for(int i=1;i<=n;++i)
		{
			scanf("%d%d",&ls[i],&rs[i]);
			in[ls[i]]++,in[rs[i]]++;
		} 
		for(int i=1;i<=n;++i)
		{
			if(!in[i])
			{
				rt=i;
				break;
			}
		}
		dfs(rt);
		dfs2(rt,1,n);
		ll ans=0;
		for(int i=1;i<=n;++i)
		ans=(ans+(res[i]^i)*1ll*bs[i]%mod)%mod;
		printf("%lld\n",ans);
	}
	return 0;
}
/*
2
5
0 0
0 5
1 0
2 3
0 0
5
0 0
0 0
0 4
2 0
1 3
*/ 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值