ACM-ICPC 2018徐州 I题 Rikka with Sorting Networks(搜索)

I. Rikka with Sorting Networks

time limit per test

4.0 s

memory limit per test

1024 MB

input

standard input

output

standard output

Rikka knows that Bubble sort is a simple but beautiful algorithm, Quicksort is a complex but efficient algorithm, and Shellsort is a weird but practical algorithm. Rikka is interested in all sorting algorithms and she can assign as many new problems for ICPC contests as she wants.

Rikka hates those guys who create new problems with the same ideas over and over again, and she hopes not to become the person she hates to be. Though she has already assigned several problems for sorting algorithms such as Merge sort and Insertion sort, she decides to show you the last problem about sorting algorithms to end this series forever.

Here Rikka introduces the sorting network and she defines a comparator at first. For a permutation AA of the nn smallest positive integers denoted by a1,a2,⋯,ana1,a2,⋯,an, a comparator [u,v][u,v] (u≠vu≠v) sorts the uu-th and the vv-th element in AA into nondecreasing order. Formally, a comparator is a mapping [u,v][u,v] satisfying

  • [u,v](au)=min(au,av)[u,v](au)=min(au,av); and
  • [u,v](av)=max(au,av)[u,v](av)=max(au,av); and
  • [u,v](ak)=ak[u,v](ak)=ak for all kk with k≠uk≠u and k≠vk≠v.

Rikka defines a sorting network as a composition of comparators and provides for you a sorting network with kk ordered comparators. Now, Rikka wants you to count the number of permutations of 11 to nn which, through the given sorting network, would become an almost sorted permutation. She says a permutation of 11 to nn is almost sorted if the length of its longest increasing subsequence is at least (n−1)(n−1).

Input

The input contains several test cases, and the first line contains a single integer TT (1≤T≤1001≤T≤100), the number of test cases.

For each test case, the first line contains three integers nn (2≤n≤502≤n≤50), the length of permutations, kk (0≤k≤100≤k≤10), the number of comparators, and qq (108≤q≤109108≤q≤109), a prime number for the output.

Then kk lines follow, the ii-th line of which contains two integers uu and vv (1≤u

Output

For each test case, output a single line with a single integer, the remainder of the number of permutations which meet the requirement divided by qq.

Example

input

4
4 0 998244353
4 1 998244353
1 2
4 3 998244353
1 2
2 3
1 2
4 6 998244353
1 2
2 3
1 2
3 4
2 3
1 2

output

10
14
24
24

题目大意:

给定n(<= 50)和 k(<=10)个有顺序比较排序器(作用为比较a[i] a[j],如果逆序则交换位置)。求有多少由1-n共n个数字构成的排列满足“大致有序”,或按顺序经过k个比较排序器后“大致有序”。“大致有序”的概念是该排列有一个长度为n-1的子序列严格递增。

解题思路:

首先判断如何构造”大致有序“的排列。假设n = i时有F[i]种”大致有序“的排列,n = i+1时,满足要求的排列可由:

1.n = i时的答案在末尾放置i+1。 (F[i]种)

2.在1,2,3……,i排列的任意一个数字之前放置i+1。 (i种)

3.将1,2,3……,i排列中除n外任意一个数字移至末尾,然后将i+1放置于最后一个数字之前 (i-1种)

得到

故F[i+1] = F[i]+i+i-1, F[i] = (i-1)^2+1;

故符合”大致有序“的排列个数在n^2级别。又因为k非常小,故可以考虑对k个比较器和暴力。

O(n^2)构造“大致有序”的排列,从”大致有序“的排列向原始排列推导:从第k个比较器开始向前搜索,假使搜索到第j个比较器时,其对应的两个整数有序,则原始排列可能使用了(上一步的排列无序)该比较器也可能没使用(上一步的排列有序),假使对第j个比较器对应的两个整数无序,则该条道路搜索不通(无序->有序,有序->有序,通过该比较器后不可能仍然无序),对搜索到的序列用SET或map判重,可得到第一版代码,复杂度O((n^2 )* (2^n) * (nlogn)),分别为枚举构造“大致有序”的排列,搜索排序器,查询判重的复杂度;

#include
#include
#include
#include
#include
#include
using namespace std;

int T, n, K, p;
vector q, qq;
set > m;
int c[11][2], ans = 0;

void dfs(int x) {
	if(x == K+1) {
		if(m.find(q) == m.end()) {
			ans += 1;
			m.insert(q);
		}
		return;
	}
	if(q[c[K-x+1][0]] < q[c[K-x+1][1]]) {
		swap(q[c[K-x+1][0]], q[c[K-x+1][1]]);
		dfs(x+1);
		swap(q[c[K-x+1][0]], q[c[K-x+1][1]]); 
		dfs(x+1);
	}
}

int main() {
	scanf("%d", &T);
	while(T--) {
		ans = 0;
		m.clear();
		scanf("%d%d%d", &n, &K, &p);
		for(int i = 1; i <= K; i++) {
			scanf("%d%d", &c[i][0], &c[i][1]);
			c[i][0]--; c[i][1]--;
		}
		q.clear();
		for(int i = 1; i <= n; i++) q.push_back(i);
		m.insert(q);
		dfs(1);
		
		for(int i = 1; i <= n; i++) {
			 
			for(int j = 1; j <= n; j++) {
				if(i == j)	continue;
				q.clear();
				for(int k = 1; k <= n; k++) {
					if(i < j && k < i) q.push_back(k);
					if(i < j && k >= i && k < j) q.push_back(k+1);
					if(i < j && k == j) q.push_back(i);
					if(i < j && k > j) q.push_back(k);
					
					if(i > j && k < j) q.push_back(k);
					if(i > j && k == j) q.push_back(i);
					if(i > j && k > j && k <= i) q.push_back(k-1);
					if(i > j && k > i) q.push_back(k);
				}
				 
				if(m.find(q) == m.end()) {
					m.insert(q);
					ans++;
					dfs(1);
				}
			
			}
			
		}
		ans++;
		ans = ans%p;
		printf("%d\n", ans);
	}
	return 0;
}

很遗憾这样会T掉。

 

问题出在”判重“这个操作上。

考虑每个原始排列通过”排序器“后能且只能得到一种新排列。故对每个”大致有序“的排列搜索时,两个”大致有序“的排列得到的原始排列必然不可能重复。又同一序列在经过排序器组时每个排序器是否使用是确定的,所以同一个”大致有序“的排列搜索到的原始排列必然不可能重复。所以搜索时判重操作可以去掉。

修改后的代码如下,复杂度O(n^2 * 2^n);

#include
#include
#include
#include
#include
#include
using namespace std;

int T, n, K, p;
vector q, qq;
set > m;
int c[11][2], ans = 0;

void dfs(int x) {
	if(x == K+1) {
		ans += 1;
		return;
	}
	if(q[c[K-x+1][0]] < q[c[K-x+1][1]]) {
		swap(q[c[K-x+1][0]], q[c[K-x+1][1]]);
		dfs(x+1);
		swap(q[c[K-x+1][0]], q[c[K-x+1][1]]); 
		dfs(x+1);
	}
}

int main() {
	scanf("%d", &T);
	while(T--) {
		ans = 0;
		m.clear();
		scanf("%d%d%d", &n, &K, &p);
		for(int i = 1; i <= K; i++) {
			scanf("%d%d", &c[i][0], &c[i][1]);
			c[i][0]--; c[i][1]--;
		}
		q.clear();
		for(int i = 1; i <= n; i++) q.push_back(i);
		m.insert(q);
		dfs(1);
		
		for(int i = 1; i <= n; i++) {
			 
			for(int j = 1; j <= n; j++) {
				if(i == j)	continue;
				q.clear();
				for(int k = 1; k <= n; k++) {
					if(i < j && k < i) q.push_back(k);
					if(i < j && k >= i && k < j) q.push_back(k+1);
					if(i < j && k == j) q.push_back(i);
					if(i < j && k > j) q.push_back(k);
					
					if(i > j && k < j) q.push_back(k);
					if(i > j && k == j) q.push_back(i);
					if(i > j && k > j && k <= i) q.push_back(k-1);
					if(i > j && k > i) q.push_back(k);
				}
				 
				if(m.find(q) == m.end()) {
					m.insert(q);
					dfs(1);
				}
			}
			
		}
		ans = ans%p;
		printf("%d\n", ans);
	}
	return 0;
}

 

你可能感兴趣的:(搜索)