2023ICPC网络预选赛 ( 2 ) (2) C.Covering【2-SAT、前后缀虚拟节点区间连边】

本文主要是介绍2023ICPC网络预选赛 ( 2 ) (2) C.Covering【2-SAT、前后缀虚拟节点区间连边】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C.Covering

1

题意

给定一个长度为 n n n 的正整数数组 a a a,现在要从中选择一些下标,满足:

  1. 对于每个下标 i i i i i i i − 1 i - 1 i1 至少 有一个被选
  2. 对于所有选择的下标,任意两个下标 i , j ( i ≠ j ) , a [ i ] ≠ a [ j ] 或 a [ i + 1 ] ≠ a [ j + 1 ] i,j(i \neq j),\hspace{4pt} a[i] \neq a[j] 或 a[i + 1] \neq a[j+1] i,j(i=j),a[i]=a[j]a[i+1]=a[j+1]
  3. 不能选择下标 n n n

分析可以发现:下标 1 1 1 一定要被选,因为它前面没有下标了,下标 n − 1 n - 1 n1 一定要被选,因为下标 n n n 不能选

考虑 2 − S A T 2-SAT 2SAT

  • 对于限制 1 1 1:连边 i ˉ → i − 1 \bar i \rarr i - 1 iˉi1 i ˉ → i + 1 \bar i \rarr i + 1 iˉi+1
  • 对于限制 2 2 2:连边 i → 集合 S i \rarr 集合 S i集合S,集合 S = ( j , ∀ j ≠ i , a [ j ] = a [ i ] ∧ a [ j + 1 ] = a [ i + 1 ] ) S = (j, \forall j \neq i, \hspace{3pt} a[j] = a[i] \wedge a[j + 1] = a[i + 1]) S=(j,j=i,a[j]=a[i]a[j+1]=a[i+1])
  • 由于 1 1 1 必选, n n n 必不选,因此可以连边: 1 ˉ → 1 \bar 1 \rarr 1 1ˉ1 n → n ˉ n \rarr \bar n nnˉ,用以确定是否存在可行解

如果对于限制 2 2 2 暴力连边,复杂度太高,考虑优化:

可以发现对于每种键值 ( a [ i ] , a [ i + 1 ] ) (a[i], a[i + 1]) (a[i],a[i+1]),它们要连的集合都是除了自己本身以外的拥有相同键值的所有点,这里会分割成一个前缀后缀

2
例如上图, A A A 值表示拥有这个键值的所有下标(前面下划线表示反变量) A 3 A_3 A3 需要连接 A ˉ 1 \bar A_1 Aˉ1 A ˉ 2 \bar A_2 Aˉ2,我们只需要将其连向 p r e 2 pre_2 pre2 A 3 → p r e 2 A_3 \rarr pre_2 A3pre2 就可以完成前缀的连边,同时 p r e 3 → A ˉ 3 pre_3 \rarr \bar A_3 pre3Aˉ3 p r e 3 → p r e 2 pre_3 \rarr pre_2 pre3pre2,以便后续相同键值的连边

后缀也是类似

需要注意的是:新建虚拟节点并不是严格按照正反变量的奇偶位置来排列的,而是紧挨着
所以我们判断可行解,只需要判断到 n n n 即可,否则会因为虚拟节点而干扰正确答案!

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n' 
#define ull unsigned long long
#define sz(x) (int)x.size()const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;typedef long long ll;struct TwoSat {int n; //属性数量std::vector<std::vector<int>> e;std::vector<bool> ans;TwoSat(int n) : n(n), e(2 * n), ans(n) {} //下标从0开始/* 建边表示 u为f 且 v为g */void addedge(int u, bool f, int v, bool g) { //原变量和反变量相邻放e[2 * u + !f].push_back(2 * v + g); //反变量在偶数位置,原变量在奇数位置e[2 * v + !g].push_back(2 * u + f);}bool satisfiable(int nn) {std::vector<int> id(2 * n, -1), dfn(2 * n, -1), low(2 * n, -1);std::vector<int> stk;int now = 0, cnt = 0;std::function<void(int)> tarjan = [&](int u) {stk.push_back(u);dfn[u] = low[u] = now++;for (auto v : e[u]) {if (dfn[v] == -1) {tarjan(v);low[u] = std::min(low[u], low[v]);} else if (id[v] == -1) {low[u] = std::min(low[u], dfn[v]);}}if (dfn[u] == low[u]) {int v;do {v = stk.back();stk.pop_back();id[v] = cnt;} while (v != u);++cnt;}};for (int i = 0; i < 2 * n; ++i) if (dfn[i] == -1) tarjan(i);for (int i = 0; i < nn; ++i) {if (id[2 * i] == id[2 * i + 1]) return false;ans[i] = id[2 * i] > id[2 * i + 1]; //取依赖性更高的那个}return true;}std::vector<bool> get_ans() { return ans; }
};const ll P = 1000001;
const int N = 200050;int yes(int x) {return 2 * x + 1;}
int no(int x) {return 2 * x;}int main(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);int n;std::cin >> n;std::vector<int> a(n);TwoSat ts(2 * n + 2);fore(i, 0, n)	std::cin >> a[i];int tot= 2 * n - 1;std::map<std::pair<int, int>, int> pre;std::map<std::pair<int, int>, int> suf;ts.e[0].push_back(1);ts.e[2 * n - 1].push_back(2 * n - 2);fore(i, 0, n){if(i > 0)	ts.addedge(i - 1, 1, i, 1);if(i < n - 1)	ts.addedge(i, 1, i + 1, 1);if(i < n - 1){if(pre.find({a[i], a[i + 1]}) != pre.end()){ts.e[2 * i + 1].push_back(pre[{a[i], a[i + 1]}]);ts.e[tot + 1].push_back(pre[{a[i], a[i + 1]}]);}pre[{a[i], a[i + 1]}] = ++tot;ts.e[tot].push_back(2 * i);}}for(int i = n - 2; i >= 0; --i){if(suf.find({a[i], a[i + 1]}) != suf.end()){ts.e[2 * i + 1].push_back(suf[{a[i], a[i + 1]}]);ts.e[tot + 1].push_back(suf[{a[i], a[i + 1]}]);}suf[{a[i], a[i + 1]}] = ++tot;ts.e[tot].push_back(2 * i);}if(!ts.satisfiable(n)){std::cout << "NO\n";return 0;}auto v = ts.get_ans();std::vector<int> ans;fore(i, 0, n - 1)if(v[i])ans.push_back(i + 1);std::cout << ans.size() << endl;for(auto i : ans)	std::cout << i << ' ';std::cout << endl;return 0; 
}/*
https://pintia.cn/problem-sets/1705510247604809728/exam/problems/1705514248467492866?type=7&page=0
*/

这篇关于2023ICPC网络预选赛 ( 2 ) (2) C.Covering【2-SAT、前后缀虚拟节点区间连边】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/891795

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

poj 2112 网络流+二分

题意: k台挤奶机,c头牛,每台挤奶机可以挤m头牛。 现在给出每只牛到挤奶机的距离矩阵,求最小化牛的最大路程。 解析: 最大值最小化,最小值最大化,用二分来做。 先求出两点之间的最短距离。 然后二分匹配牛到挤奶机的最大路程,匹配中的判断是在这个最大路程下,是否牛的数量达到c只。 如何求牛的数量呢,用网络流来做。 从源点到牛引一条容量为1的边,然后挤奶机到汇点引一条容量为m的边

hdu4267区间统计

题意:给一些数,有两种操作,一种是在[a,b] 区间内,对(i - a)% k == 0 的加value,另一种操作是询问某个位置的值。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import

hdu4417区间统计

给你一个数列{An},然后有m次查询,每次查询一段区间 [l,r] <= h 的值的个数。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamRead