ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JZOJ3483. 囚人的旋律

2021-05-05 15:36:28  阅读:168  来源: 互联网

标签:连边 ch 旋律 int read 囚人 序列 JZOJ3483 逆序


题目大意

给出一个由 \(n\)个数的排列, 逆序对之间连边 的方式构造出来的无向图, 求图中既是独立集又是覆盖集的点集的个数.
形式化的, 若\(i<j, p_i > p_j\), 则在图中连一条边\((i,j)\).

\(n <= 1000\).

解题思路

不难想到把图转化为序列上来做. 那么图中的边集转化成了序列上的一个子序列.
考虑计数既是独立集又是覆盖集的子序列的个数.

首先考虑独立集: 根据独立集中点之间没有边相连, 我们又有相连的边代表着一对逆序对, 说明在这个子序列中没有逆序对. 那是什么? 那是单调递增序列.

接下来考虑覆盖集: 假设相邻的选取的两个点是\(l, r\ (l < r)\). 由前面可知\(p_r > p_l\).

首先地, 在\((l,r)\)之间的点, 必定要么与\(l\)有边相连, 要么与\(r\)有边相连.
为什么? 自己手推一下不难发现, 这样的限制是最松的. 也就是说, 根据选出的子序列的单调性, 在\((l,r)\)中能与\(l\)之前的点或\(r\)之后的点连边的, 必能与\(l\)连边或与\(r\)连边.

然后, 注意到\((l,r)\)之间的点可分为两类. 一类是与\(r\)连边, 也就是\(p_i > p_r\), 一类是与\(l\)连边, 也就是\(p_i < p_l\).
于是不合法的点满足\(p_i\in (p_l, p_r)\).

分析出这些东西, 显然dp. 设\(f_i\)表示前\(i\)个数组成的子序列, 其中必选\(i\). 特别地, 设\(f_0=1\), 有如下转移式:

\[\Huge f_i=\sum_{j\in[0,i), \forall k \in (j,i) \text{满足}p_k \notin (p_j, p_i)}f_j \]

在实现中可以记录小于\(p_i\)的最大值, \(j\)指针从右往左扫, 顺便更新最大值和dp值, 过程中与\(p_j\)比较.

最后一个问题: 如何将给出的图转化为序列?
给出的图相当于给出了一堆偏序关系. 然而靠这些偏序关系并不能唯一确定这个序列. 所以其实言外还隐藏着 除了这些逆序对外, 不存在其他逆序对 的信息.
于是考虑连边拓扑, 按拓扑序 从大到小分配\(p_i\), 在拓扑排序中把队列换成大跟堆, 这样就能保证逆序对不增多.

时间复杂度\(O(n\log n+n^2)\), 瓶颈在于dp. 以及似乎有\(O(n)\)转化的方法, 在此不赘述.

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1010
#define MOD 1000000007
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
inline int read() // notice : 1. long long ? 2. negative ?
{
	int x = 0; char ch = getchar();
	while(ch < '0' || ch > '9')	ch = getchar();
	while(ch >= '0' && ch <= '9')	x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return x;
}
priority_queue<int> h;
int n, m, t[N][N], a[N], f[N], in[N];
void top_sort()
{
	fo(i, 1, n)	if(!in[i])	h.push(i);
	int p = n;
	while(!h.empty())
	{
		int u = h.top(); h.pop();
		a[u] = p--;
		fo(v, 1, n)	if(t[u][v])	if(!(--in[v]))	h.push(v);
	}
}
int main()
{
	n = read(), m = read();
	int u, v; 
	fo(i, 1, m)	u = read(), v = read(), t[min(u, v)][max(u, v)] = 1, ++in[max(u, v)];
	top_sort();
	f[0] = 1, a[n + 1] = 0x3f3f3f3f;
	fo(i, 1, n + 1)
	{
		int mx = -1;
		if(a[i] > a[i - 1])	f[i] = f[i - 1], mx = a[i - 1];
		fd(j, i - 2, 0)
		{
			if(a[i] > a[j] && a[j] > mx)	f[i] = (f[i] + f[j]) % MOD, mx = max(mx, a[j]);
		}
	}
	printf("%d\n", f[n + 1]);
	return 0;
}

标签:连边,ch,旋律,int,read,囚人,序列,JZOJ3483,逆序
来源: https://www.cnblogs.com/Martin-MHT/p/14731806.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有