• JZOJ 6979. 【2021.02.03冬令营模拟】天各一方(DP)


    JZOJ 6979. 【2021.02.03冬令营模拟】天各一方

    题目大意

    • n n n个点组成的所有不同连通图中, 1 1 1 n n n的最短距离之和。
    • n ≤ 400 nle400 n400

    题解

    • 很关键的一点是,因为是所有连边的方案,所以 1 1 1 n n n 1 1 1 2 2 2 1 1 1 3 3 3…… 1 1 1 n − 1 n-1 n1本质上都是相同的,所以答案可以转化为 1 1 1到剩下每个点的最短距离之和再除以 n − 1 n-1 n1

    • 试着把所有的点分层,距离即为它们层数的差值。初始时均在第 0 0 0层,通过DP使除了 1 1 1以外所有点下移。

    • f i , j f_{i,j} fi,j为当前确定好了 i i i个点,其中有 j j j个点确定了放在最后一层,而剩下的 n − i n-i ni此时也被移到了最后一层,但还将继续下移的方案数, g i , j g_{i,j} gi,j表示对应的总和。注意这里并不需要记录层数,因为它对转移无影响。

    • 转移时枚举下一层留下多少个 k k k,为了保证层数能对应距离,所以下一层只能和当前层连边,不能和上一层连边,同时层内可以自由连边,于是有:

    • f i , i + k = ∑ f i , j ∗ ( n − i k ) ∗ ( 2 j − 1 ) k ∗ 2 k ( k − 1 ) 2 f_{i,i+k}=sum f_{i,j}*{n-ichoose k}*(2^j-1)^k*2^{frac{k(k-1)}{2}} fi,i+k=fi,j(kni)(2j1)k22k(k1)

    • g i , i + k = ∑ ( g i , j + ( n − i ) ∗ f i , j ) ∗ ( n − i k ) ∗ ( 2 j − 1 ) k ∗ 2 k ( k − 1 ) 2 g_{i,i+k}=sum ig(g_{i,j}+(n-i)*f_{i,j}ig)*{n-ichoose k}*(2^j-1)^k*2^{frac{k(k-1)}{2}} gi,i+k=(gi,j+(ni)fi,j)(kni)(2j1)k22k(k1)

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 410
    #define ll long long
    int c[N][N], e[N * N], s[N][N], P;
    ll f[N][N], g[N][N];
    ll ksm(ll x, ll y) {
    	if(!y) return 1;
    	ll l = ksm(x, y / 2);
    	if(y % 2) return l * l % P * x % P;
    	return l * l % P;
    }
    int main() {
    	int n, i, j, k;
    	scanf("%d%d", &n, &P);
    	e[0] = 1;
    	for(i = 1; i <= n * n; i++) e[i] = e[i - 1] * 2 % P;
    	for(i = 0; i <= n; i++) {
    		c[i][i] = c[i][0] = s[i][0] = 1;
    		for(j = 1; j < i; j++) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % P;
    		for(j = 1; j <= n; j++) s[i][j] = (ll)s[i][j - 1] * (e[i] - 1) % P;
    	}
    	f[1][1] = 1, g[1][1] = 0;
    	for(i = 1; i < n; i++) {
    		for(j = 1; j <= i; j++) {
    			for(k = 1; i + k <= n; k++) {
    				ll t = (ll)c[n - i][k] * s[j][k] % P * e[k * (k - 1) / 2] % P;
    				(f[i + k][k] += f[i][j] * t) %= P;
    				(g[i + k][k] += g[i][j] * t + f[i][j] * t % P * (n - i)) %= P;
    			}
    		}
    	}
    	ll ans = 0;
    	for(i = 1; i <= n; i++) (ans += g[n][i]) %= P;
    	printf("%lld
    ", ans * ksm(n - 1, P - 2) % P);
    	return 0;
    }
    

    自我小结

    • 对若干个等价的方案之一计数,可以先计算总和再除以相应的个数。
    • 和图或树有关的计数题可以从小数据入手,尽可能多地设出图的各种性质作为状态,尝试转移,最后再考虑优化。
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    对协程的一些理解
    JUC中Lock和ReentrantLock介绍及源码解析
    JUC中AQS简介
    Executor框架简介
    CyclicBarrier简介
    CountDownLatch简介
    Semaphore简介
    ThreadPoolExecutor简介
    AtomicInteger简介
    synchronized和volatile简介
  • 原文地址:https://www.cnblogs.com/LZA119/p/14437600.html
Copyright © 2020-2023  润新知