CF1877 E. Autosynthesis 基环树dp

news2024/11/17 23:02:00

传送门:CF

[前题提要]:一道基环树dp,但是题目有点绕,当时卡了我整整半天,到了第二天换了和清醒的脑子然后和别人讨论才整明白,故记录一下

题目很绕,故不再介绍.

首先对于这种下标和值有关系的题目.其实不难想到建图(CF上有大量这种 t r i c k trick trick),随便举个类似的题目:CF1768D.所以我们考虑每一个位置下标连向值建图.(注意我们建出来的图中的所有点都是点下标之前的关系)

首先,按照套路,每一个点都有且只有一条出边,也就是每一个点的出度都为1,那么这张图其实是一棵内向基环树.

然后想一下这样建图有什么意义.我们发现我们圈出一个点,其实多出来的是这个点的下标的贡献,那么我们想要选出这个点,按照题意,我们得需要一个值为我们选出来的点的下标的点留下来.那么这个点在哪里呢.就是我们选出来的这个点的入点.(请仔细理解这一部分).

所以我们想要选出一个点,必须得存在一个入点留下来.

然后我们考虑一个点想要留下来.要满足什么条件.我们一个点留下来了,多出来的是这个点的值的贡献,所以我们得需要一个下标为这个值的点被选出来.这个点在哪里呢,这个点显然就是我们当前点的出点.(因为我们每一个点都指向下标为该点值的点).


总结一下就是:
一个点想要留下来,它的父亲必须删除.一个点想要删除,必须存在一个儿子留下来

然后根据上述结论,其实我们不难发现,叶子节点都是得保留下来的,因为没有一个点指向叶子节点,也就是,没有一个点的值等于我们的叶子结点的下标

所以我们考虑对这棵基环树进行拓扑(其实这也是基环树dp的经典套路,拓扑排序).从叶子节点开始倒推,一步一步的确定每一个点的状态(此时我们会发现一个点的状态其实是一定的,因为有一个点只要有儿子留下来,该点就必须删除,没有儿子留下来的时候,该点又只能留下来).所以我们可以使用树形 d p dp dp来推出每一个根节点的状态(保留或者删除).

这里补一下上述做法:因为该图是一个基环树,按照经典做法,我们考虑求出环上的每一个点,那么对于每一个点来说,他都是一棵树的根节点(基环树的性质).所以我们可以对每一个根节点形成的树进行树形 d p dp dp.然后求出每一个根节点的状态最后进行环形 d p dp dp.

所以我们现在是求出了环上每一个点的状态.所以我们考虑环上该怎么办.
详细讨论一下就会发现存在以下几种情况:

  1. 环上的每一个点独立状态都是保留.此时需要注意的是,我们环上的点其实相互都是有入点和出点的.所以此时的保留状态其实是可以变为删除状态的.此时我们只要随便选一个点保留,然后对应的出点需要删除.显然的,我们会发现只要是奇数点必然会出现矛盾,是偶数环则不会.
  2. 环上存在一个点是删除状态的.我们考虑从这个删除状态出发.因为环上一个点如果删除了.它的出点就不会有这个点的贡献.所以出点如果是删除状态,那么还是删除状态;如果是保留状态,那么需要变为删除状态,然后使用之前的性质递推下去.这里需要注意的是,我们需要从删除状态出发.

举个栗子:

在这里插入图片描述

0代表独立状态为删除,1代表保留.如果我们从下面那个3出发,我们会发现4变为0,3也变为0.但是当3变为0的时候,4不能变为0.所以此时出现了问题.
但是为什么从0出发就没有问题呢.因为从0出发,我们最后循环到自己这个位置的时候因为他是0,所以无论后面是1还是0,他都可以是0(不会对初始状态产生影响).所以我们会发现这样构造一定是成立的.

还有一点需要注意的是,本题可能是一个基环树森林


下面是具体的代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
inline void print(__int128 x){
	if(x<0) {putchar('-');x=-x;}
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
#define maxn 1000000
#define int long long
const int mod=998244353;
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];vector<int>edge[maxn];
int in[maxn];int n;int vis[maxn],mp[maxn];int cnt=0;
void dfs(int u,int per_u) {//找环
	for(auto v:edge[u]) {
		if(v==per_u) continue;
		if(in[v]==1||vis[v]==1) continue;
		mp[++cnt]=v;vis[v]=1;
		dfs(v,u);
	}
}
int state[maxn];//1保留,0删除
void solve(int u,int per_u) {//树形dp
	if(edge[u].size()==1) {
		state[u]=1;return ;
	}
	int this_cnt=0;
	for(auto v:edge[u]) {
		if(v==per_u) continue;
		if(in[v]==2) continue;
		solve(v,u);
		this_cnt+=state[v];
	}
	if(this_cnt!=0) state[u]=0;
	else state[u]=1;
}
void init() {
	for(int i=1;i<=cnt;i++) {
		mp[i]=0;
	}
}
int flag=0;
void topo() {
	queue<int>q;
	for(int i=1;i<=n;i++) {
		if(in[i]==1) {
			q.push(i);
		}
	}
	while(!q.empty()) {
		int u=q.front();q.pop();
		for(auto v:edge[u]) {
			in[v]--;
			if(in[v]==1) {
				q.push(v);
			}
		}
	}
	for(int i=1;i<=n;i++) {
		if(in[i]==2&&vis[i]==0) {
			cnt=0;
			mp[++cnt]=i;vis[i]=1;
			dfs(i,0);
			for(int j=1;j<=cnt;j++) {
				solve(mp[j],0);
			}
			int pos=0;int this_cnt=0;
			for(int j=1;j<=cnt;j++) {
				this_cnt+=state[mp[j]];
				if(state[mp[j]]==0) {
					pos=j;
				}
			}
			if(this_cnt==cnt) {//全是1,说明可以乱填,但如果是奇数,不行
				if(cnt&1) {
					flag=1;break;
				}
				else {
					int num=1;
					for(int j=1;j<=cnt;j++) {
						state[mp[j]]=num;num^=1;
					}
				}
			}
			else {//如果不是,那么如果一个点没有儿子,那这个1不能变成0
				//反之可以变为0(因为环中也可以保留点),所以应该从0出发
				for(int j=pos;j<=pos+cnt-1;j++) {
					if(state[mp[(j-1+cnt)%cnt+1]]==1) {
						if(state[mp[(j+cnt)%cnt+1]]) {
							state[mp[(j+cnt)%cnt+1]]=0;
						}
					}
				}
			}
			if(flag) break;
			init();
		}
	}
	if(flag) {
		cout<<-1<<endl;
	}
	else {
		int this_cnt=0;
		for(int i=1;i<=n;i++) {
			if(state[i]==1) {
				this_cnt++;
			}
		}
		cout<<this_cnt<<endl;
		for(int i=1;i<=n;i++) {
			if(state[i]==1) {
				cout<<a[i]<<" ";
			}
		}
		cout<<endl;
	}
}
signed main() {
//	freopen("input.in","r",stdin);
//	freopen("output.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) {
		a[i]=read();
		edge[i].push_back(a[i]);
		edge[a[i]].push_back(i);
		in[i]++;in[a[i]]++;
	}
	topo();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1276170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TZOJ 1429 小明A+B

答案&#xff1a; #include <stdio.h> int main() {int T0, A0, B0, sum0;scanf("%d", &T); //输入测试数据的组数while (T--) //循环T次{scanf("%d %d", &A, &B); //输入AB的值sum A B;if (sum > 100) //如果是三位数{…

VR全景对旅游业有什么帮助,如何助力旅游业实现新的旅游形式

引言&#xff1a; 旅游业是一个充满机遇的行业&#xff0c;而虚拟现实&#xff08;VR&#xff09;全景技术正逐渐改变着旅游业的面貌&#xff0c;通过提供身临其境的体验&#xff0c;VR全景成为了旅游业的新宠&#xff0c;将旅游带入了一个全新的数字化时代。 一、打破地域限制…

SpringMVC利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值

在我们进行Java的Web应用开发时&#xff0c;如何写更少的代码&#xff0c;做更多的事情。如何让开发更容易上手&#xff0c;更专注于业务层面&#xff0c;不需要太关心底层的实现。这里就分享一些我平时在搭建基础框架时候的一些心得体验。 统一处理返回值 在web应用中&#x…

elupload base64

创作灵感也许就是这会儿还没有入睡吧&#xff0c;对接百度图片OCR功能&#xff0c;需要将图片转为BASE64上传调用百度的接口api&#xff0c;进行研究实现。页面如下&#xff0c;点击后选择图片文件后不是直接上传&#xff0c;而是获取图片的bytes数据&#xff01; <el-uploa…

【开源】基于JAVA的大病保险管理系统

项目编号&#xff1a; S 031 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S031&#xff0c;文末获取源码。} 项目编号&#xff1a;S031&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统配置维护2.2 系统参保管理2.3 大…

详解十大经典排序算法(二):选择排序(Selection Sort)

算法原理 选择排序通过重复选择数组中最小元素&#xff0c;将其与未排序部分的第一个元素交换&#xff0c;实现排序。 算法描述 选择排序是一种简单的排序算法&#xff0c;它每次从待排序的元素中选择最小&#xff08;或最大&#xff09;的元素&#xff0c;将其放到已排序序列的…

NodeJs(一):初识nodejs、模块化、CommonJS、ESModule等

目录 (一)Nodejs简介 1.nodejs是什么 2.nodejs架构 3.nodejs的应用场景 (二)准备工作 1.安装nodejs 2.nodejs版本管理工具 (三)nodejs的使用 1.node的输入 2.node的输出 3.其他的console方法 (四)全局对象 1.常见的全局对象 2.特殊的全局对象 3.global和window的…

单片机----汇编语言入门知识点

目录 汇编语句的格式 汇编语句的两个基本语句 子程序的调用 查表程序设计 1.x和y均为单字节数的查表程序设计 2.x为单字节数y为双字节数的查表程序设计 3.x和y均为双字节数的查表程序设计 分支转移程序设计 1.单分支选择结构 2.多分支选择结构 循环程序设计 (1) 计…

算法通关村第一关—白银挑战—链表高频面试算法题—查找两个链表的第一个公共子节点

文章目录 查找两个链表的第一个公共子节点&#xff08;1&#xff09;暴力求解法&#xff08;2&#xff09;使用哈希Hash⭐&#xff08;3&#xff09;使用集合⭐ - 与Hash类似&#xff08;4&#xff09;使用栈⭐&#xff08;5&#xff09;仍有更多方法&#xff0c;作者尚未理解&…

时间序列预测实战(二十二)TCN-LSTM实现单元和多元长期预测(专为新手编写的自研架构)

一、本文介绍 本篇文章给大家带来的是利用我个人编写的架构进行TCN-LSTM时间序列卷积进行时间序列建模&#xff08;专门为了时间序列领域新人编写的架构&#xff0c;简单不同于市面上用GPT写的代码&#xff09;&#xff0c;包括结果可视化、支持单元预测、多元预测、模型拟合效…

Burp Suite序列之目录扫描

如果你是一名渗透测试爱好者或者专业人士&#xff0c;你一定知道目录扫描是渗透测试中非常重要的一步。通过目录扫描&#xff0c;我们可以发现网站的敏感信息&#xff0c;隐藏的功能&#xff0c;甚至是后台入口。目录扫描可以帮助我们更好地了解目标网站的结构和漏洞。 但是&a…

如何访问电脑的组策略编辑器?

如何打开组策略 如果我们使用的是 Win 10 系统&#xff0c;如何打开组策略&#xff1f;下面为大家总结了四种打开组策略编辑器的方法。 从搜索框打开 Win 10 策略组怎么打开&#xff1f;一个简单快速的方法就是使用 Windows 自带的搜索栏。我们可以向搜索框中输入“编辑组策…

Vue3中的组合式API的详细教程和介绍

文章目录 前言介绍组合式 API 基础setup 组件选项 带 ref 的响应式变量生命周期钩子注册内部 setupwatch 响应式更改独立的 computed 属性后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;vue.js &#x1f431;‍&#x1f453;博主在前端…

探索创意无限的Photoshop CC 2020Mac/Win版

作为一款功能强大的图像处理软件&#xff0c;Photoshop CC 2020&#xff08;简称PS 2020&#xff09;在全球范围内备受设计师、摄影师和艺术家的喜爱和推崇。它不仅为用户提供了丰富多样的工具和功能&#xff0c;还不断推出新的创意特效和改进的功能&#xff0c;让用户的创意无…

链式栈的结构与基本操作的实现(初始化,入栈,出栈,获取元素个数,判空,清空,销毁)

目录 一.链式栈的栈顶在哪里? 二.链栈的结构: 三.链式栈的实现: 四.链式栈的总结: 一.链式栈的栈顶在哪里? 二.链栈的结构: typedef struct LSNode{int data;struct LSNode* next;}LSNode ,*PLStack; //链栈的节点.由于栈顶在第一个数据节点,所以不需要top指针 三.链式…

细粒度视觉分类的注意内核编码网络

Attentional Kernel Encoding Networks for Fine-Grained Visual Categorization 1、介绍2、方法2.1 卷积模块2.2 级联注意力模块2.3 内核编码模块2.4 整体 3、结论 在本文中&#xff0c;我们提出了一种用于细粒度视觉分类的注意核编码网络(AKEN)。具体来说&#xff0c;AKEN聚合…

【性能测试】性能分析和调优——步骤及案例

文章目录 性能测试瓶颈分析常见的性能瓶颈分析性能调优性能调优步骤 性能调优案例案例一——CPU案例二——网络案例三——SQL查询案例四——JVM内存溢出 阅读前建议先了解前一篇文章&#xff1a;【性能测试】性能测试监控关键指标 性能测试瓶颈分析 常见的性能瓶颈分析 1、服…

qemu网络通信

TAP&#xff08;官网参考地址&#xff09; TAP&#xff0c;即Tunneling traffic access point&#xff0c;是一种在Linux上使用的虚拟网卡技术&#xff0c;它可以为应用程序提供安全的网络连接。可以利用TAP搭建桥接网络&#xff0c;bridge两端分别为host和qemu虚拟机。 安装…

带删除的并查集

Almost Union-Find 支持三种操作 合并 x x x和 y y y所在的集合把 x x x移到 y y y所在的集合求 x x x所在的集合的元素个数和元素之和 操作1和3是基本的并查集的操作. 关键在于操作 2 2 2: 若使用朴素的并查集&#xff0c;把节点 1 1 1合并到 3 3 3所在的集合&#xff0c;会…

人机协同

人机协同是指人和机器之间进行合作和协同工作的方式&#xff0c;人机协同是人工智能技术发展的一个重要方向&#xff0c;通过人机协同的方式&#xff0c;可以充分利用机器的智能和人的智慧&#xff0c;共同实现更高效、更智能的工作和生活方式。人机协同可以应用于各种领域和场…