• 11.16 校内模拟赛解题报告


    为什么还要考试啊,我的天啊!!!!

    T1

    二分 + 贪心。

    可以发现,一行的数是连续的肯定会让这一行最大值减去最小值最小。

    对数组进行排序,进行二分答案,用连续的数铺每一行,看能否铺满。
    来个好看的代码。

    int k, n, m, ans, a[maxn]; 
    bool check(int mid)
    {
    	int js = 0;
        for(int i = 1; i + m - 1 <= k; i++)
            if(a[i + m - 1] - a[i] <= mid) js++, i += m - 1;
        if(js >= n) return 1;
        return 0;
    }
    int main()
    {
    	k = read(), n = read(), m = read();
    	if(m == 1) {puts("0"); return 0;}
    	for(int i = 1; i <= k; i++) a[i] = read();
    	sort(a + 1, a + k + 1);
    	int l = 0, r = a[k];
    	while(l <= r)
    	{
    		int mid = (l + r) >> 1;
    		if(check(mid)) r = mid - 1, ans = mid;
    		else l = mid + 1;
    	}
    	printf("%d", ans);
    	return 0;
    }
    

    T2

    60 pts

    \(n ^3\) 的DP。

    \(f[i][j]\) 表示前 \(i\)​ 个数分成 \(j\) 段所得到的最大价值。

    \(f[i][j] = max(f[k][j - 1] + sum[k + 1][i])\)​。

    	n = read(), K = read();
    	for(int i = 1; i <= n; i++) a[i] = read();
    	for(int i = 1; i <= n; i++) 
    		for(int j = i; j <= n; j++)
    			sum[i][j] = (sum[i][j - 1] | a[j]);
    	for(int i = 1; i <= n; i++) f[i][1] = sum[1][i];
    	for(int i = 1; i <= n; i++)
    	{
    		for(int j = 1; j < i; j++)
    		{
    			for(int k = 1; k <= Min(i, K); k++)
    			{
    				f[i][k] = Max(f[i][k], f[j][k - 1] + sum[j + 1][i]);
    			}	
    		}
    	}
    	printf("%d", f[n][K]);
    

    100 pts

    可以发现,\(sum\) 数组有一段一段的数是相同的。

    \(f\) 数组肯定是单调递增的,对于每个固定的 \(i\) 点,\(sum[k+1][j]\) 的取值最多只有 \(32\)​ 种。

    预处理出转移点。

    int n, K, a[maxn], sum[maxn][maxn], f[maxn][1000]; // 把前 i个数分成 j块 
    vector<int> e[maxn];
    int main()
    {
    	//freopen("b.in", "r", stdin);
    	//freopen("b.out", "w", stdout);
    	n = read(), K = read();
    	for(int i = 1; i <= n; i++) a[i] = read();
    	for(int i = 1; i <= n; i++) 
    		for(int j = i; j <= n; j++)
    			sum[i][j] = (sum[i][j - 1] | a[j]);
    	for(int i = 1; i <= n; i++)
    	{
    		e[i].push_back(i);
    		for(int j = i - 1; j >= 1; j--)
    		if(sum[j][i] != sum[j + 1][i]) e[i].push_back(j); 
    	}
    	for(int i = 1; i <= n; i++)
    	{
    		for(int k = 1; k <= Min(i, K); k++)
    		{
    			for(int j = 0; j < e[i].size(); j++)
    			{
    				int v = e[i][j];
    				f[i][k] = Max(f[i][k], f[v - 1][k - 1] + sum[v][i]);	
    			}
    		}	
    	}
    	printf("%d", f[n][K]);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    T3

    30pts

    暴力枚举全排列。

    60pts

    对于这棵二叉树设最下边一层为第 \(0\)​ 层,设编号 \(i\) 与编号 \(j\) 在第 \(k\) 层相遇,设 \(i < j\)

    对于第 \(k\)​ 层,每个点对应的叶子节点为 \(2 ^ k\)

    对于\(i\)​ 的子树,现在已知最大的一个编号为 \(i\)​, 还剩下 \(2^k - 1\)​ 个节点,那么这棵子树中所有的情况就是 \(C(2^k - 1, i - 1)\)​​ 。

    对于 \(j\)​​ 的子树,以为已知 \(i < j\)​ ,所以 \(i\)​ 子树内所有的编号一定小于 \(j\)​ 的子树内的,所以 \(j\)​ 的子树内编号为 \(j - 1 - 2 ^ k\)​,所有的情况就是 \(C(2 ^k - 1, j- 1 - 2^k )\)

    上边讨论的是编号的情况,又因为编号是随机排列的,所以子树内的编号的排列顺序也是不固定的,还要乘以两个\(2 ^k\) !。

    对于一整颗二叉树,除了刚刚讨论的 \(i, j\) 子树中的点, 还有 \(n - 2 ^{k + 1}\) 个点, 这些点的排列也是随机的,所以再再乘以一个 \(n - 2 ^{k + 1}\)​ !。

    刚刚讨论的是一个点对,要算出这一层中有多少个点对,就是 \(2 ^{K- k - 1}\)​​个。

    因为这是 \(i < j\) 的情况, 还有 \(i > j\) 的情况,所以还要乘以 \(2\)

    最后再乘上 \(i, j\) 的权值。

    枚举所有的 \(i, j\) ,它们的总贡献就是

    \[\sum_{k = 0}^{K - 1}2^{K - k} \times (n - 2^{k + 1})! \times 2^{k}! \times 2^{k}! \times \displaystyle\sum_{i=1}^{n} \sum_{j = i + 1}^{n}\binom{i-1}{x} \times \binom{j-1 - 2^{k}}{x} \times a_i \times a_j \]

    100 pts

    考虑前面这个东西做一个前缀和。

    \[\displaystyle\sum_{i=1}^{n} \binom{i-1}{x} \times a_i \sum_{j = i + 1}^{n} \binom{j-1 - 2^{k}}{x}\times a_j \]

    时间复杂度为 \(O(k 2 ^k)\)​。

    隔壁 zxs AC代码。

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int MAXN = 3e5 + 5;
    const int MOD = 1e9 + 7;
    int read() {
    	int x = 0, f = 1;char c = getchar();
    	while(c < '0' || c > '9' ) {if (c == '-') f = -1;c = getchar();}
    	while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
    	return x * f;
    }
    int K, Val[MAXN], N, Fac[MAXN], ret, inv[MAXN], sum[MAXN];
    int Qpow(int x, int y) {
    	int ret = 1;
    	while(y) {
    		if(y & 1) ret = (ret * x) % MOD;
    		y >>= 1;
    		x = (x * x) % MOD;
    	}
    	return ret;
    }
    int C(int n, int m) {
    	if (m > n) return 0;
    	return Fac[n] * inv[m] % MOD * inv[n - m] % MOD;
    }
    signed main() {
    	//freopen("c4.in", "r", stdin);
    	//freopen("c.out", "w", stdout);
    	K = read();
    	N = (1 << K), Fac[0] = 1, inv[0] = 1;
    	for (int i = 1; i <= N; i++) Fac[i] = Fac[i - 1] * i % MOD;
    	for (int i = 1; i <= N; i++) inv[i] = Qpow(Fac[i], MOD - 2);
    	for (int i = 1; i <= N; i++) Val[i] = read();
    	for (int k = 0, tmp; k < K; k++) {
    		tmp = (1 << (K - k)) * Fac[N - (1 << k + 1)] % MOD;
    		tmp = tmp * Fac[(1 << k)] % MOD * Fac[(1 << k)] % MOD;
    		int all = 0;
    		for (int j = N; j >= 1; j--) {
    		  sum[j] = (sum[j + 1] + C(j - 1 - (1 << k), (1 << k) - 1) * Val[j] % MOD) % MOD;
    		}
    		for (int i = 1; i <= N; i++) 
    		  all = (all + C(i - 1, (1 << k) - 1) % MOD   * Val[i] % MOD * sum[i + 1] % MOD) % MOD;
    		tmp = tmp * all % MOD;
    		ret = (ret + tmp) % MOD;
    	}
    	int tot = Fac[N];
    	cout<<ret * Qpow(tot, MOD - 2) % MOD;
    	return 0;
    }
    
  • 相关阅读:
    MSN无法登录(错误代码80072745)的解决方法
    C#3.0新体验(二) 扩展方法
    My DreamTech
    让IE崩溃的bug, IE8也一样崩溃
    多线程的相关概念
    10条PHP经验总结
    PHP框架 CI与TP之MVC比较
    多线程设计要点
    Linux yum命令的使用技巧
    BigPipe 的工作原理
  • 原文地址:https://www.cnblogs.com/yangchengcheng/p/15562383.html
Copyright © 2020-2023  润新知