首页 > 其他分享 >Living-Dream 系列笔记 第53期

Living-Dream 系列笔记 第53期

时间:2024-04-13 21:44:55浏览次数:23  
标签:pre Living int 53 stk 括号 dfs Dream dp

妙妙题大合集。

T1

令 \(dp_{i,j}\) 表示分离出以 \(i\) 为根的恰含 \(j\) 节点的树所需的最小删边数。

有初始状态 \(dp_{i,1}=\) 其子节点个数,其余为 \(\infty\)。

对于答案,我们考虑到对于每个节点 \(i\),除了其子树内的删边数之外,它的父节点与它的连边也应删去(注意根节点 \(root\) 无需考虑)。

于是每个节点的答案为

\[\begin{cases} dp_{i,p}+1 \ \ \ i \neq root \\ dp_{i,p} \ \ \ i=root \end{cases} \]

对所有节点的答案取 \(\min\) 即为最终答案。

对于转移,我们需要保留根节点与其儿子的连边,即要删除的边少一条。

于是有转移

\[dp_{x,v}=\min(dp_{x,v},dp_{i,k}+dp_{x,v-k}-1) \]

(\(x\) 为当前根,\(v\) 为背包容量,\(i\) 为 \(x\) 的儿子,\(k\) 为 \(i\) 的子树内留下的节点数,下同)

code
#include<bits/stdc++.h>
using namespace std;

const int N=2e2+5; 
int n,p;
int dp[N][N];
vector<int> G[N<<1];

int dfs(int x){
	int siz=1;
	dp[x][1]=G[x].size();
	for(int i:G[x]){
		int son=dfs(i); siz+=son;
		for(int v=min(siz,p);v>0;v--)
			for(int k=0;k<=min(v-1,son);k++)
				dp[x][v]=min(dp[x][v],dp[i][k]+dp[x][v-k]-1);	
	}
	return siz;
}

int main(){
	memset(dp,0x3f,sizeof(dp));
	cin>>n>>p;
	for(int i=1,u,v;i<n;i++)
		cin>>u>>v,
		G[u].push_back(v);
	dfs(1);
	int ans=dp[1][p];
	for(int i=2;i<=n;i++) ans=min(ans,dp[i][p]+1);
	cout<<ans;
	return 0;
} 

T2

首先考虑一个弱化版问题:

给定一个括号串 \(t\),定义 \(s_i\) 为 \(t_{1 \sim i}\),对于所有的 \(i\),若 \(s_i\) 中有 \(k_i\) 个互不相同的子串为合法括号串,求 \(k_i\)。

对于该问题,我们令 \(dp_i\) 表示以 \(i\) 结尾的合法括号串数量。

根据 \(dp_i\) 的定义可知,\(k_i=\sum^i_{j=1} dp_j\)。

考虑状态转移。

首先 \(dp_i\) 发生状态转移当且仅当 \(t_i\) 为右括号,因为只有在此时才会发生左右括号匹配。

我们令与当前右括号匹配的左括号的位置为 \(pre\),则有转移方程

\[dp_i=dp_{pre-1}+1 \]

这个转移方程的实质即为将以 \(pre-1\) 结尾的合法括号串与当前的括号串拼接在一起,形成了一种新的合法括号串,在加上以 \(pre-1\) 结尾的 \(dp_{pre-1}\) 种合法括号串,就得到了 \(dp_i\)。

然后,我们只需要用一个栈维护 \(pre\) 即可。

将这个序列问题搬到树上做,就成了本题。

具体地,我们仍令 \(dp_i\) 表示以 \(i\) 结尾的合法括号串数量。

根据 \(dp_i\) 的定义可知,\(k_i=\sum^i_{j=1} dp_j\)。

仍然考虑状态转移。

我们令与当前右括号匹配的左括号的位置为 \(pre\),则有转移方程

\[dp_i=dp_{fa_{pre}}+1 \]

注意此处 \(pre\) 的前一个并非 \(pre-1\),而是 \(fa_{pre}\)。

我们同样采用栈维护 \(pre\) 即可。

不同的是,树上的每个左括号都可能有多个右括号与之匹配,而在序列上是唯一的。

于是我们考虑找完一条路径后就回溯。

具体而言,我们用一个标记 \(f\) 标记当前的右括号是(\(1\))否(\(0\))被匹配。

在回溯时,若 \(f=1\),则将其配对的左括号重新进栈;

对于未匹配成功的左括号,则应将其从栈内弹出。

然后这题就做完了。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=5e5+5;
int n;
int dp[N],fa[N];
string s;
vector<int> G[N];
stack<int> stk;

void dfs(int x){
	int pre,f=0; dp[x]=0;
	if(s[x]=='(') stk.push(x);
	else if(!stk.empty())
		pre=stk.top(),stk.pop(),f=1,
		dp[x]=dp[fa[pre]]+1;
	for(int i:G[x]) dfs(i);
	if(f) stk.push(pre);
	else if(s[x]=='(') stk.pop();
}
void getsum(int x){
	for(int i:G[x]) dp[i]+=dp[x],getsum(i);
}

signed main(){
	cin>>n>>s,s="#"+s;
	for(int i=2;i<=n;i++)
		cin>>fa[i],G[fa[i]].push_back(i);
	dfs(1),getsum(1);
	int ans=0;
	for(int i=1;i<=n;i++) ans^=(i*dp[i]);
	cout<<ans;
	return 0;
}

T3

本题使用朴素选边 / 枚举点对 + LCA 求距离 的方式,以 \(O(C_{n}^{k} \times n^2 \log n)\) 的优秀时间复杂度,即可拿到 \(0\) 分的好成绩。

考虑如何进行优化。

我们想到,对于每一条边,计算它被那哪些点对间的路径经过,累加贡献即可。

具体地,我们令 \(dp_{i,j}\) 表示以 \(i\) 为根的子树内有 \(j\) 个黑点时的最大贡献。

答案显然为 \(dp_{1,k}\)。

对于初始状态,有 \(dp_{i,0}=dp_{i,1}=0\)(因为这两种情况始终合法),其余均为 \(-\infty\)。

对于状态转移,我们将子树外的黑点数 \(\times\) 子树内的黑点数 \(\times\) 边权即为该边对黑点的贡献。白点同理。

注意开 long long 并建双向边即可。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=2e3+5;
int n,k;
int dp[N][N];
struct Edge{ int to,w; };
vector<Edge> G[N];

int dfs(int x,int f){
	int siz=1; dp[x][0]=dp[x][1]=0;
	for(Edge i:G[x]){
		if(i.to==f) continue;
		int son=dfs(i.to,x); siz+=son;
		for(int v=min(siz,k);v>=0;v--){
			for(int p=0;p<=min(son,v);p++){
				int black=p*(k-p)*i.w;
				int white=(son-p)*(n-k-son+p)*i.w;
				dp[x][v]=max(dp[x][v],dp[i.to][p]+dp[x][v-p]+black+white);
			}
		}
	}
	return siz;
}

signed main(){
	memset(dp,0xcf,sizeof(dp));
	cin>>n>>k;
	for(int i=1,u,v,w;i<n;i++)
		cin>>u>>v>>w,
		G[u].push_back({v,w}),
		G[v].push_back({u,w});
	dfs(1,-1);
	cout<<dp[1][k];
	return 0;
}

标签:pre,Living,int,53,stk,括号,dfs,Dream,dp
From: https://www.cnblogs.com/XOF-0-0/p/18133343

相关文章

  • P5311 Ynoi2011 成都七中
    P5311Ynoi2011成都七中点分树好题,太妙了。思路看到树和连通块,考虑点分树。但是从这里发现原树和点分树的联系实在太小,貌似不可做。可以发现对于一个询问,一个点如果和\(x\)在一个连通块内,那么这个点到\(x\)的最大最小节点编号肯定都在\([l,r]\)这个范围内。可以证明,......
  • 实验2 C语言分支与循环基础应用编程 王刚202383310053
    1#include<stdio.h>2#include<stdlib.h>3#include<time.h>4#defineN55intmain()6{7intnumber,i;8srand(time(0));9for(i=0;i<N;i++)10{number=rand()%65+1;11printf("20238331%04d\n",number);12}13sy......
  • ZCMU-1053
    比较简单记录一下主要感觉它这个题目没说清楚,题目要求:先有n,接着给出长度为n的标准组,然后给出猜测组,输出的两个数一个是有多少个是相对应的既相同坐标其数值也相同,后一个是两个都有但是位置不同(不含已经相同的)我觉得它少了一类个例子:类似于123436133343思路:用......
  • App Store 警告 ITMS-91053: Missing API declaration
    问题:app虽然成功上架AppStore,但是邮件提示了如下警告:解决:解决方法是添加隐私清单文件。参考官方说明:官方文档其它相关链接:StackOverflow中关于这个问题的讨论这位作者分享了如何解决该问题这篇文章提供了解决该问题详细的指南......
  • #莫队二次离线,根号分治#洛谷 5398 [Ynoi2018] GOSICK
    题目\(m\)组询问求\(\sum_{l\leqi,j\leqr}[a_i\bmoda_j==0],n,m,a_i\leq5\times10^5\)分析设\(f(l,r,x)\)表示\(i或j\in[1,x],i或j\in[l,r]\)时的答案,\(g_x\)表示\([1,x]\)的答案,根号的做法可以通过三秒由于涉及区间内的求值,需要在莫队的基础上二次离线,那......
  • GD32F470_VL53L0X激光测距传感器模块移植
    2.15VL53L0X激光测距传感器VL53L0X是ST公司推出的新一代ToF激光测距传感器,采用了第二代FlightSenseTM技术,利用飞行时间(ToF)原理,通过光子的飞行来回时间与光速的计算,实现测距应用。较比上一代VL6180X,新的器件将飞行时间测距长度扩展至2米,测量速度更快,能效更高。除此......
  • P5327 [ZJOI2019]语言 解题报告
    oj:https://gxyzoj.com/d/gxyznoi/p/P35树上差分+线段树合并+树链剖分1.暴力从最基础的方法开始,暴力统计s-t上的点,然后用map直接统计,显然会T,20pts2.特殊性质测试点5,6保证了数据是一条链,将链上每一个节点编号为1-n对于每一次操作,可以记录其覆盖情况,设共有x个节点,有y个节......
  • 2-53. 种子数据库制作
    创建DropDetails创建CropDataList_SO项目相关代码代码仓库:https://gitee.com/nbda1121440/farm-tutorial.git标签:20240410_0932......
  • 【ZZULIOJ】1053: 正弦函数(Java)
    目录题目描述输入输出样例输入 Copy样例输出 Copycode题目描述输入x,计算上面公式的前10项和。输入输入一个实数x。输出输出一个实数,即数列的前10项和,结果保留3位小数。样例输入 Copy1样例输出 Copy0.841codeimportjava.util.*;publicclassMain......
  • 20天【代码随想录算法训练营34期】第六章 二叉树part07 ( ● 530.二叉搜索树的最小绝对
    530.二叉搜索树的最小绝对差#Definitionforabinarytreenode.#classTreeNode:#def__init__(self,val=0,left=None,right=None):#self.val=val#self.left=left#self.right=rightclassSolution:deftraversal(self,......