zoukankan      html  css  js  c++  java
  • [CSA] Number Elimination

    Solution

    要求最小代价的方案数,所以我们显然可以直接把这些元素从小到大排序

    我们令(f_i)表示消去(i)个一样的数字的方案数,不难得出(f_i = frac {i cdot (i - 1)} {2} f_{i - 1})

    假设当前有(i)个数字,我们可以任选两个数字把编号小的消去,所以方案数为(i choose 2)

    然后就转化成(i - 1)个数字的局面了

    把值相等的数字分为一组,设(len_i)表示第(i)组数字的个数,设(sum_i)表示前(i)数字的总长度,(dp_i)表示消去前(i)组的方案数,则有:

    [dp_i = dp_{i - 1} cdot f_{len_i} cdot sum _{j = 0} ^ {j = len_i - 1} {{sum_{i - 1} - 1 + j} choose j} cdot (len_i - j) ]

    前面乘了一个(f_{len_i})表示已经考虑了本组的内部消除顺序

    组合数表示删掉前面的所有数(除上组最后一个)以及当前组的任意(j)个数的先后顺序方案数

    最后后面那个式子表示上组最后一个可以被当前组剩下未删的任意一个数消去

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define fst first
    #define snd second
    #define squ(x) ((LL)(x) * (x))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    typedef long long LL;
    typedef pair<int, int> pii;
    
    inline int read() {
    	int sum = 0, fg = 1; char c = getchar();
    	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
    	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    	return fg * sum;
    }
    
    const int maxn = 1e5 + 10;
    const int mod = 1e9 + 7;
    
    inline int add(int x, int y) { return (x += y) < mod ? x : x - mod; }
    inline int mul(LL x, int y) { return (LL)x * y % mod; }
    
    int Pow(int x, int y) {
    	int res = 1;
    	while (y) {
    		if (y & 1) res = mul(res, x);
    		x = mul(x, x); y >>= 1;
    	}
    	return res;
    }
    
    int n, N, a[maxn], len[maxn], sum[maxn], dp[maxn], f[maxn];
    
    int fac[maxn], ifac[maxn];
    
    inline int C(int _n, int _m) { return mul(fac[_n], mul(ifac[_n - _m], ifac[_m])); }
    
    void init() {
    	fac[0] = 1;
    	for (int i = 1; i <= n; i++) fac[i] = mul(fac[i - 1], i);
    	ifac[n] = Pow(fac[n], mod - 2);
    	for (int i = n - 1; ~i; i--) ifac[i] = mul(ifac[i + 1], i + 1);
    }
    
    int main() {
    #ifdef xunzhen
    	freopen("out.in", "r", stdin);
    	freopen("out.out", "w", stdout);
    #endif
    
    	n = read(); init();
    	for (int i = 1; i <= n; i++) a[i] = read();
    
    	sort(a + 1, a + n + 1);
    	int pos = 1;
    	for (int i = 2; i <= n + 1; i++)
    		if (a[i] != a[i - 1]) len[++N] = i - pos, sum[N] = sum[N - 1] + len[N], pos = i;
    
    	f[1] = 1;
    	for (int i = 2; i <= n; i++) f[i] = mul(f[i - 1], mul(mul(i, i - 1), ifac[2]));
    
    	dp[1] = f[len[1]];
    	for (int i = 2; i <= N; i++) {
    		int Sum = 0;
    		for (int j = 0; j < len[i]; j++)
    			Sum = add(Sum, mul(C(sum[i - 1] - 1 + j, j), len[i] - j));
    		dp[i] = mul(mul(dp[i - 1], f[len[i]]), Sum);
    	}
    
    	printf("%d
    ", dp[N]);
    
    	return 0;
    }
    

    Summary

    这个DP的思维好神啊,完全想不到 考试的时候部分分都打挂了

  • 相关阅读:
    apache与tomcat负载集群的3种方法
    JFinal 源码分析 [DB+ActiveRecord]
    通过PowerShell获取TCP响应(类Telnet)
    Linux 硬盘分区、分区、删除分区、格式化、挂载、卸载
    常用EXE文件反编译工具
    Shell采集系统cpu 内存 磁盘 网络信息
    MyEclipse运行很慢的原因
    Java令牌生成器
    Shell 编程基础之基本语法结构汇总
    Shell 编程基础之注意技巧
  • 原文地址:https://www.cnblogs.com/xunzhen/p/9844682.html
Copyright © 2011-2022 走看看