时间限制:1秒
空间限制:32768K
热度指数:5122
算法知识视频讲解
题目描述
牛牛的作业薄上有一个长度为 n 的排列 A,这个排列包含了从1到n的n个数,但是因为一些原因,其中有一些位置(不超过 10 个)看不清了,但是牛牛记得这个数列顺序对的数量是 k,顺序对是指满足 i < j 且 A[i] < A[j] 的对数,请帮助牛牛计算出,符合这个要求的合法排列的数目。
输入描述:
每个输入包含一个测试用例。每个测试用例的第一行包含两个整数 n 和 k(1 <= n <= 100, 0 <= k <= 1000000000),接下来的 1 行,包含 n 个数字表示排列 A,其中等于0的项表示看不清的位置(不超过 10 个)。
输出描述:
输出一行表示合法的排列数目。
示例1
输入
5 5 4 0 0 2 0
输出
2
#include <bits/stdc++.h> using namespace std; set<int>st; int a[111], c[111], cnt[111], b[11][111], tot, num[11], n, k, have, cc[111]; inline int lowbit(int x){ return x & (-x); } void add(int c[], int x, int v){ while(x <= n){ c[x] += v; x += lowbit(x); } } int query(int c[], int x){ int ans = 0; while(x){ ans += c[x]; x -= lowbit(x); } return ans; } bool check(){ int all = have; for(int i = 1; i <= tot; ++i){ all += b[i][num[i]]; } memset(cc, 0, sizeof(cc)); for(int i = 1; i <= tot; ++i){ all += query(cc, num[i]); add(cc, num[i], 1); } if(all == k){ return true; } else{ return false; } } int main(){ cin >> n >> k; memset(c, 0, sizeof(c)); have = 0; tot = 0; for(int i = 1; i <= n; ++i){ scanf("%d", &a[i]); if(a[i] == 0){ tot++; for(int j = 1; j <= n; ++j){ b[tot][j] = query(c, j); } continue; } cnt[a[i]] = 1; have += query(c, a[i]); add(c, a[i], 1); } memset(c, 0, sizeof(c)); int x = 0; for(int i = n; i >= 1; --i){ if(a[i] != 0){ add(c, a[i], 1); } else{ for(int j = 1; j <= n; ++j){ b[tot][j] += n - i - x - query(c, j); } x++; tot--; } } for(int i = 1; i <= n; ++i){ if(cnt[i] == 0){ st.insert(i); } } tot = 0; int ans = 0; set<int>::iterator it; for(it = st.begin(); it != st.end(); ++it){ num[++tot] = *it; } sort(num + 1, num + 1 + tot); do{ if(check()){ ans++; } }while(next_permutation(num + 1, num + 1 + tot)); cout << ans << endl; } /* 题意: 一个1到n的排列,有不超过10个数看不清了,现在让你还原这个数列,使得正序列对数为k。 思路: 这题的数据是真的弱。。我是n!log(10), 4ms过的。 先n^2log(n)预处理一下每个为0的位置填1到n的时候分别会对清楚的位置增加多少个正序列对, 然后nlog(n)求一下清楚的部分的正序列对数。 然后全排列枚举,这样对于新填的数对于清楚的数的影响可以O(1)求,然后对于新填的数的序列 求一下正序列数,加一下判断一下即可。 */