• 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;
    }
    
  • 相关阅读:
    Spring Boot -- Spring Boot之@Async异步调用、Mybatis、事务管理等
    Spring Boot -- 认识Spring Boot
    大数据 -- Cloudera Manager(简称CM)+CDH构建大数据平台
    大数据 -- kafka学习笔记:知识点整理(部分转载)
    大数据 -- Hadoop集群环境搭建
    大数据 -- zookeeper和kafka集群环境搭建
    Java基础 -- 数组
    sbt spark2.3.1 idea打包Caused by: java.lang.ClassNotFoundException: scala.Product$class
    windows 安装python pip Could not install packages due to anEnvironmentError: [WinError 5] 拒绝访问
    博客园编辑数学公式的方法
  • 原文地址:https://www.cnblogs.com/czhui666/p/13893278.html
Copyright © 2020-2023  润新知