1028考试总结

1028考试总结

T1

​ 题目大意:

​ 给定一个排列,为这个排列字典序的下一个排列是啥?\(n <= 1e5\).(不能用next_permutation.)

​ 从后往前找第一个\(x_i < x_{i + 1}\)的数,这个数\(i\)之后的数是单调递减的.也就是说这个数后面的已经是极大的了,不能通过改变顺序是后面这几个数更大,所以我们只能增大\(i\)位置上的这个数,在后面的数中找一个比它大的最小的数,让他俩交换位置,剩下没选过的数就从小到大排一遍序就好了.

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
	long long s = 0, f = 1; char ch;
	while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
	for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
	return s * f;
}

const int N = 1e5 + 5;
int n, p[N], vis[N]; 

int main() {
	
	n = read();
	for(int i = 1;i <= n; i++) p[i] = read();
	int pos;
	for(int i = n;i >= 1; i--) {
		vis[p[i]] = 1;
		if(p[i - 1] < p[i]) { pos = i - 1; break; }
	}
	if(pos == 0) for(int i = 1;i <= n; i++) printf("%d ", i);
	else {
		vis[p[pos]] = 1; int tag = 0;
		for(int i = 1;i <= n; i++) if(vis[i] && i > p[pos]) {
			vis[i] = 0; p[pos] = i; tag = 1; break;
		}
		if(!tag) {
			vis[p[pos]] = 1; vis[++ p[pos]] = 0;
		}
		for(int i = 1;i <= pos; i++) printf("%d ", p[i]);
		for(int i = 1;i <= n; i++) if(vis[i]) printf("%d ", i);
	}
	
	fclose(stdin); fclose(stdout);
	return 0;
}

T2

​ 题目大意:给定一棵树,再给出所有的叶子结点经过的顺序,然后从根节点1开始走,每条边最多走两次,最后回到根节点,问是否可以走出一条路径满足给定的经过叶子结点的顺序.不能的话输出-1,否则输出这条路径.\(n <= 1e5\).

\(LCA\) 树上问题.

​ 我们发现直接按题意模拟是可以通过时间复杂度的,因为每条边最多走两次,所以最多走\(2n - 2\)条边.所以我们按给定的叶子结点的顺序求出相邻两个叶子结点的\(LCA\).然后暴力条父亲就好了,记得记录一下边的经过次数.

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
	long long s = 0, f = 1; char ch;
	while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
	for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
	return s * f;
}

const int N = 1e5 + 5;
int n, cnt, num;
int id[N], dep[N], fa[N][21], head[N], tmp_ans[N * 10], ans[N * 10], in_edge[N];
struct edge { int to, nxt; } e[N << 1];

void add(int x, int y) {
	e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; in_edge[y] ++;
}

void get_tree(int x, int Fa) {
	dep[x] = dep[Fa] + 1; fa[x][0] = Fa; 
	int tag = 0;
	for(int i = head[x]; i ; i = e[i].nxt) {
		int y = e[i].to; if(y == Fa) continue;
		get_tree(y, x); tag = 1;
	}
	if(!tag) num ++;
}

void make_fa() {
	for(int i = 1;i <= 20; i++) 
		for(int j = 1;j <= n; j++) 
			fa[j][i] = fa[fa[j][i - 1]][i - 1];
}

int LCA(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 20;i >= 0; i--) if(dep[x] - dep[y] >= (1 << i)) x = fa[x][i];
	if(x == y) return x;
	for(int i = 20;i >= 0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}

int main() {
	
	n = read(); int tot = 0;
	for(int i = 1, x, y;i <= n - 1; i++) {
		x = read(); y = read(); add(x, y); add(y, x);
	}
	get_tree(1, 0); make_fa();
	int last = 1; tot = 0;
	for(int i = 1, x, lca;i <= num + 1; i++) {
		if(i == num + 1) x = 1; else x = read(); 
		lca = LCA(last, x);
		int tmp = last; cnt = 0;
		while(tmp != lca) { tmp_ans[++ cnt] = tmp; id[tmp] ++; tmp = fa[tmp][0]; if(id[tmp] > 2) { printf("-1"); return 0; } }
		for(int i = 2;i <= cnt; i++) ans[++ tot] = tmp_ans[i];
		ans[++ tot] = lca;
		tmp = x; cnt = 0;
		while(tmp != lca) { tmp_ans[++ cnt] = tmp; id[tmp] ++; tmp = fa[tmp][0]; if(id[tmp] > 2) { printf("-1"); return 0; } }
		for(int i = cnt;i >= 1; i--) ans[++ tot] = tmp_ans[i]; 
		last = x;
	}
	for(int i = 1;i <= tot; i++) printf("%d ", ans[i]);
	
	fclose(stdin); fclose(stdout);
	return 0;
}

T3

题目链接

​ 题目大意:给定\(n\)\(a\)\(n\)个数\(b\),求\(a > b\)的对数 - \(b > a\)的对数恰好等于\(k\)的方案数.\(n <= 2000\),答案对\(1e9 + 9\)取模.保证每个\(a, b\)都不相等.

​ 首先我们可以得到\(a > b\)的对数是\(\frac {n + k}{2}\).然后我们对\(a, b\)从小到大排序.

\(f[i][j]\)表示前\(i\)\(a\),钦定\(j\)\(a > b\)的方案数,那么DP转移方程就是:\(f[i][j] = f[i - 1][j] + f[i - 1][j - 1] * (g[i] - (j - 1))\).

​ 为什么是钦定不说恰好呢?假设当前所有\(a_i\)都大于\(b\),那么是无法恰好选出\(j\)对的,因为所有的\(a_i\)都可以与其他的\(b\)形成\(a > b\),钦定的意思是强制选取\(j\)对,其他的不管,这样之后求\(h[j]\)的时候会乘上\((n - j)!\),求出来的\(h[j]\)才是正确的.

\(g[i]\)表示有多少个\(b\)小于\(a_i\).这个转移方程的含义就是"第\(i\)个数\(a_i\)没有形成\(a > b\) + 第\(i\)个数\(a_i\)形成了\(a > b\)并且这个\(b\)\(j - 1\)\(a > b\)中没有出现过 ".

\(h[i]\)表示至少有\(i\)\(a > b\)的方案数(这里也可以说是钦定\(i\)\(a > b\),其他的不管的方案数),那么可以得到:\(h[i] = f[n][i] * (n - i)!\).剩下\(n - i\)个数随便排列,可能形成\(1\)\(n - i\)对新的\(a > b\).(这样算出的\(h[i]\)是有重复方案的对吧)

\(ans[i]\)表示恰好有\(i\)\(a > b\)的方案数,我们可以发现它和\(h[i]\)的关系:\(h[i] = \displaystyle \sum_{j = i}^{n} C_{j}^{i} ans[j]\).重新看\(h[i]\)的定义:至少有\(i\)\(a > b\)的方案数.那么我们就要把所有\(j >= i\)\(ans[j]\)统计到\(h[i]\)里面,并且要算上重复的部分,也就是在乘上\(C_{j}^{i}\)(因为钦定了\(i\)对嘛)..

​ 然后我们把\(ans[i]\)移到一边:\(ans[i] = h[i] - \displaystyle \sum_{j = i + 1}^{n} C_{j}^{i} ans[j]\).然后倒推就可以了.时间复杂度\(O(n ^ 2)\).

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
	long long s = 0, f = 1; char ch;
	while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
	for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
	return s * f;
}

const int N = 2005, mod = 1e9 + 9;
int n, k, num;
int a[N], b[N], g[N], h[N], fac[N], ans[N], inv[N], f[N][N];
 
void make_pre() {
	fac[0] = inv[0] = inv[1] = 1;
	for(int i = 1;i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % mod;	
	for(int i = 2;i <= n; i++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
	for(int i = 2;i <= n; i++) inv[i] = 1ll * inv[i - 1] * inv[i] % mod;
}

int C(int n, int m) {
	if(n < m) return 0;
	return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
 
int main() {
	 
	n = read(); k = read(); make_pre(); 
	if(((n & 1) + (k & 1)) & 1) { printf("0"); return 0; }
	num = (n + k) / 2;
	for(int i = 1;i <= n; i++) a[i] = read();
	for(int i = 1;i <= n; i++) b[i] = read();
	sort(a + 1, a + n + 1); sort(b + 1, b + n + 1);
	for(int i = 1;i <= n; i++) 
		for(int j = 1;j <= n; j++) 
			if(a[i] > b[j]) g[i] ++;
			else break;
	for(int i = 0;i <= n; i++) f[i][0] = 1;
	for(int i = 1;i <= n; i++) {
		for(int j = 1;j <= n; j++) 
			f[i][j] = (f[i - 1][j] + 1ll * f[i - 1][j - 1] * (max(0, g[i] - j + 1)) % mod) % mod;
		}
	for(int i = 0;i <= n; i++) 
		h[i] = 1ll * f[n][i] * fac[n - i] % mod;
	for(int i = n;i >= num; i--) {
		int tmp = 0;
		for(int j = i + 1;j <= n; j++) tmp = (tmp + 1ll * C(j, i) * ans[j] % mod) % mod;
		ans[i] = (h[i] - tmp + mod) % mod;
	}
	printf("%d", ans[num]);
	
	return 0;
}
posted @ 2020-10-28 20:37  C锥  阅读(112)  评论(0编辑  收藏  举报