zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    编译原理-第二章 一个简单的语法指导编译器-2.4 语法制导翻译
    编译原理-第二章 一个简单的语法指导编译器-2.3 语法定义
    编译原理-第二章 一个简单的语法指导编译器-2.2 词法分析
    LeetCode 1347. Minimum Number of Steps to Make Two Strings Anagram
    LeetCode 1348. Tweet Counts Per Frequency
    1349. Maximum Students Taking Exam(DP,状态压缩)
    LeetCode 1345. Jump Game IV(BFS)
    LeetCode 212. Word Search II
    LeetCode 188. Best Time to Buy and Sell Stock IV (动态规划)
    LeetCode 187. Repeated DNA Sequences(位运算,hash)
  • 原文地址:https://www.cnblogs.com/czhui666/p/13893278.html
Copyright © 2011-2022 走看看