• AGC054 B


    终于做出一道AGC的B了!!(虽然好像只打了一场AGC

    传送门


    题意

    有N个橘子,每个橘子有一个重量w, 现在A先开始拿第一个, 如果A的重量比B大,那么就到B开始拿。
    问有多少种橘子的排列使得拿完所有橘子后, AB拿的重量相等。


    思路

    一开始就是各种dp, 就是那种往排列里面插数的dp,很绝望。
    然后就是考虑贪心, 搜索+剪枝, 和神奇的结论,但显然不行。
    后来开始考虑发过来构造,开始思考既然两个人拿的一样多, 那是不是只要把橘子分成两份一样多的就行了。


    题解

    好欸, 就是这样。
    你想啊,我们把橘子分成两堆一样多的, 保证a拿到都是堆1的, b都是2的。
    这样重量是一样的, 但是能保证一定有这样的排列吗?
    可以的, A拿第一个, 我们把堆1的第一个放入排列, 如果A比B多, 那么就依次放堆2的, 再到堆1。
    因为两堆橘子都会拿完(重量一样,读者可自行证明。)所以一定可以产生合法排列。
    但不是唯一的哦, 假设堆1是(1, 2, 3)A可以先拿1, 也可以先拿2...这样产生的排列是不一样的。

    所以, 我们把所有橘子划分成相同的两堆排列, (1, 2, 3)表示先A拿1,再拿2...(2, 3, 1)则算另一种分法, 对于每一种分法, 我们只能构造出唯一的合法序列(读自证, 所以答案就是划分方案数。

    然后开始dp, (f_{i, j, sum})表示在前i个橘子种拿了j个,总重量为sum的方案数。
    这个dp转移显然,读者自己写

    处理出这个dp数组后, 枚举一共拿了i个橘子, 和为全部橘子重量一半的方案数(记为fi
    预处理阶乘fact数组。

    那么对于每个i, (ans) += (f_{i} * (fact[i] * fact[n-i]))
    详细的说, fi表示把橘子分成两堆,堆1有i个, 堆2有n-i个。的方案数
    对于每种方案, 堆1显然有(i!)种排列, 堆2有((n-i)!)种。
    如果还不懂,看看代码,或者自己思考一下?
    是不是太详细了
    但这道题的我考场确实没太想明白(虽然a了, 但oi不能自我欺骗。
    要把每一步的证明都思考明白

    	for(int i=1; i<=n; i++){
    		f[i-1][0][0] = 1;
    		for(int j=1; j<=i; j++){
    			for(int k=0; k<=N*N; k++){
    				f[i][j][k] = f[i-1][j][k];
    				if(w[i] <= k) f[i][j][k] += f[i-1][j-1][k-w[i]];
    				f[i][j][k] %= mod;
    			}
    		}
    	}
    	
    	p[1] = 1;
    	for(int i=2; i<=n; i++){
    		p[i] = p[i-1] * i;
    		p[i] %= mod;
    	}
    	
    	if(sum % 2){
    		printf("0
    ");
    		return 0;
    	}
    	sum/=2;
    	
    	ll ans = 0;
    	for(int i=1; i<=n; i++){
    		ans += f[n][i][sum] * ((p[i] * p[n-i])%mod);
    		ans %= mod;
    	}
    	printf("%lld
    ", ans);
    
    
  • 相关阅读:
    Pycharm如何自动换行
    Android逆向基础
    动态调试ELF文件Crackme
    用Hash 算法给payload瘦身
    010 editor手写pe文件
    win脱壳_压缩壳_aspack
    WannaCrypt0r分析报告
    JVM内存分区
    java设计模式(模板方法模式)
    es6
  • 原文地址:https://www.cnblogs.com/ltdjcoder/p/14968763.html
Copyright © 2020-2023  润新知