• 【luogu P7967】Magneti(DP)


    Magneti

    题目链接:luogu P7967

    题目大意

    给你 n 个物品,每个有一个值,而且物品之间不同。
    然后有一个长度为 L 的板子,然后你要把物品放在每个整点上面,使得两个物品之间的距离不小于它们权值的最大值。
    然后问你有多少种方法。

    思路

    考虑 DP,发现不太能 DP,因为跟两个有关,但是不可以 2 n 2^n 2n

    考虑转化一下,发现如果我们确定了顺序,那最少要长度为多少的板子呢?
    思考一下会发现两个之间占了 max ⁡ ( a i , a i + 1 ) \max(a_i,a_{i+1}) max(ai,ai+1) 的空位,那总共要的位置就是这些的和加上 n n n
    那多出的位置我们可以用插板法来分配。

    那问题就变成了求对于每个 max ⁡ \max max 的和的结果,有多少种顺序对应呢?
    考虑贡献的位置,也就是大的那个,于是考虑从小到大加数,那每次跟前面的多少个相邻就是贡献多少个自己。
    然后考虑放进去之后会有若干块,块之间不相邻,之后再用数合并。
    然后就列出一个 DP: f i , j , k , 0 / 1 / 2 , w f_{i,j,k,0/1/2,w} fi,j,k,0/1/2,w 为前 i i i 个数放进去,一边空着的 j j j 个块,两边空着的 k k k 个块,两段的块已经放了 0 / 1 / 2 0/1/2 0/1/2 个,当前 max ⁡ \max max 的和为 w w w
    然后分类讨论转移。

    发现会超时,因为是 O ( n 3 L ) O(n^3L) O(n3L) 的。
    考虑优化,观察一下转移的式子会发现 j j j 只有在两端的时候才会加,所以 j j j 的大小其实也是 0 / 1 / 2 0/1/2 0/1/2,然后变成 O ( n 2 L ) O(n^2L) O(n2L) 就可以过了。

    代码

    #include
    #include
    #define mo 1000000007
    
    using namespace std;
    
    const int N = 51;
    const int M = 1e4 + 10; 
    int n, L, a[N], jc[M], inv[M], invs[M];
    int f[2][3][N][3][M], inv2;
    
    int add(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
    int dec(int x, int y) {return x < y ? x - y + mo : x - y;}
    int mul(int x, int y) {return 1ll * x * y % mo;}
    int C(int x, int y) {if (y < 0 || y > x) return 0; return mul(jc[x], mul(invs[y], invs[x - y]));}
    int ksm(int x, int y) {int re = 1; while (y) {if (y & 1) re = mul(re, x); x = mul(x, x); y >>= 1;} return re;}
    
    void Init() {
    	jc[0] = 1; for (int i = 1; i < M; i++) jc[i] = mul(jc[i - 1], i);
    	inv[0] = inv[1] = 1; for (int i = 2; i < M; i++) inv[i] = mul(inv[mo % i], mo - mo / i);
    	invs[0] = 1; for (int i = 1; i < M; i++) invs[i] = mul(invs[i - 1], inv[i]);
    	inv2 = ksm(2, mo - 2);
    }
    
    void ck(int &x, int y) {
    	x = add(x, y);
    }
    
    int main() {
    //	printf("%.3lf", (sizeof(f) * 2) / 1024.0 / 1024.0);
    	
    	freopen("magnet.in", "r", stdin);
    	freopen("magnet.out", "w", stdout);
    	
    	Init();
    	
    	scanf("%d %d", &n, &L);
    //	for (int i = 1; i <= n; i++) a[i] = 1;
    	for (int i = 1; i <= n; i++) scanf("%d", &a[i]); sort(a + 1, a + n + 1);
    	
    	if (n == 1) {
    		printf("%d", L); return 0;
    	}
    	
    	f[0][0][0][0][0] = 1;
    	for (int i = 1; i <= n; i++) {
    		for (int j = 0; j < 3; j++)
    			for (int k = 0; k + j < i; k++)
    				for (int l = 0; l <= 2; l++)
    					for (int s = 0; s <= L; s++)
    						if (f[(i & 1) ^ 1][j][k][l][s]) {
    							int x = f[(i & 1) ^ 1][j][k][l][s]; f[(i & 1) ^ 1][j][k][l][s] = 0;
    							if (l < 2) {
    								ck(f[i & 1][j + 1][k][l + 1][s], (2 - l) * x);
    								if (s + a[i] <= L) {
    									if (j) ck(f[i & 1][j - 1][k][l + 1][s + a[i]], mul(x, j));
    									if (k) ck(f[i & 1][j + 1][k - 1][l + 1][s + a[i]], mul(x, k * (2 - l)));
    								}
    							}
    							if (s + a[i] <= L) {
    								if (j) ck(f[i & 1][j][k][l][s + a[i]], mul(x, j));
    								if (k) ck(f[i & 1][j][k][l][s + a[i]], mul(x, 2 * k));
    							}
    							if (s + a[i] + a[i] <= L) {
    								if (j > 1) ck(f[i & 1][j - 2][k][l][s + a[i] + a[i]], x);
    								if (k > 1) ck(f[i & 1][j][k - 1][l][s + a[i] + a[i]], mul(x, mul(k, k - 1)));
    								if (j && k) ck(f[i & 1][j][k - 1][l][s + a[i] + a[i]], mul(x, mul(j, k)));
    							}
    							ck(f[i & 1][j][k + 1][l][s], x);
    						}
    	}
    	
    	int ans = 0;
    	for (int i = 0; i <= L; i++) {
    		ck(ans, mul(f[n & 1][0][0][2][i], C(L - i + n - 1, n)));
    	}
    	printf("%d", ans);
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
  • 相关阅读:
    springboot线程池创建与使用
    AutoRunner4.5结合真实项目crm客户管理系统进行界面功能自动化测试教学资料(登录)
    Linux之httpd及虚拟主机的配置及使用
    react-router基本使用
    MySQL 教程:MySQL IN 语句(高级)
    Threejs_04 gui调试开发
    『忘了再学』Shell基础 — 32、Shell中test测试命令详解
    经典SQL语句大全
    [LeetCode/力扣][Java] 0315. 计算右侧小于当前元素的个数(Count of Smaller Numbers After Self)
    不必安装,快速设计数据库表结构
  • 原文地址:https://blog.csdn.net/weixin_43346722/article/details/126064492