zoukankan      html  css  js  c++  java
  • agc022F

    题目大意

    题解

    非常玄学的dp

    首先一个很显然的东西,因为把x按照y翻转得到2y-x,所以每个点的最终系数为±2^k

    假设有两种不同的方案得到同样的接,那么相减之后把正的放一边负的放另一边,因为10^100非常大以至于把其他的加在一起都不行,因此只要方案不同答案就不同

    转化模型,从y向x连一条边,变成一个由有向边构成的树,每次选一条边u->v,把u所在连通块*2,v所在连通块取反

    可以写成一棵权值为2^深度的树,然后从后往前选边,考虑dp,然后就不会了

    网上有各种玄学做法,根据个人理解讲一下比较常见的那种


    一个点的权值=(-1)^(儿子个数+某个祖先在选完相连链之后选的儿子个数),这样会好考虑一些

    由于一个点的权值只会和儿子和祖先有关,因此在最后一层加上一个点后向上只会影响到其父亲,向下的影响等价于放之前父亲祖先的影响,即把点权设为父亲当前的点权,然后把父亲以及之前加入的兄弟节点取反

    设f[i,j]表示已经放了i个点,最后一层有j个儿子个数为奇数的方案

    假设一个点有s个儿子,那么有s/2上取整(网上的都写成了下取整)个点和父亲不同

    枚举当前层的点数k,有(j+k)/2个点与父亲不同

    再枚举实际不同的个数l,那么新放的这一层有|l-(j+k)/2|个奇点,乘的系数是C(i+k,i)*C(k,l)

    以上是网上的题解,下面开始个人看法


    首先如果是简单的“不同”,那么实际上+,-变成-+-,+-的时候当不同个数为2时有+++,+-以及---,--两种情况,这两种都要算进去,这里感觉非常奇怪

    并且更加奇怪的是,不同的个数为什么可以直接丢到组合数里面算

    所以状态f[i,j]中的j,实际表示的应该是有j个同一种符号奇点的方案数,符号是硬点的某一种(+或-都可以),并且可以改变,具体见下文

    还是上面的例子,+,-变成-+-,+-,因为奇点是+所以硬点的符号是+

    所以枚举的l的实际意义是当前硬点的符号的个数,由于奇点符号全部相同,所以如果不变(下一层奇点为0)的话那个数就是(j+k)/2,变了之后下一层的奇点个数就是|l-(j+k)/2|

    这样就可以在l=0以及l=4把上面的情况区分开来,同时由于l表示的是符号个数所以可以直接组合数

    观察一下,如果l>(j+k)/2的话那么是把非硬点符号改成硬点符号,<(j+k)/2是把硬点符号改成非硬点符号,因为改的符号相同且改过的都是奇点,所以下一层的符号就是改过之后的符号,也是唯一确定的

    如此归纳下去,最后得到的就是正确的结果

    END

    时间复杂度O(n^4)

    code

    #include <bits/stdc++.h>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define C(n,m) ((jc[n]*Jc[m])%1000000007*Jc[(n)-(m)]%1000000007)
    #define add(a,b) a=((a)+(b))%1000000007
    #define abs(x) ((x)>0?(x):-(x))
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define mod 1000000007
    #define Mod 1000000005
    #define ll long long
    //#define file
    using namespace std;
    
    ll f[51][51],jc[51],Jc[51];
    int n,i,j,k,l,x;
    
    ll qpower(ll a,int b) {ll ans=1; while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}
    
    int main()
    {
    	#ifdef file
    	freopen("agc022f.in","r",stdin);
    	#endif
    	
    	scanf("%d",&n);
    	jc[0]=1;
    	fo(i,1,n) jc[i]=jc[i-1]*i%mod;
    	Jc[n]=qpower(jc[n],Mod);
    	fd(i,n-1,0) Jc[i]=Jc[i+1]*(i+1)%mod;
    	
    	f[1][0]=f[1][1]=1;
    	fo(i,1,n-1)
    	{
    		fo(j,0,i)
    		{
    			fo(k,max(1,j),n-i)
    			{
    				fo(l,0,k)
    				if (!((j+k)&1))
    				{
    					x=(j+k)/2;
    					add(f[i+k][abs(x-l)],f[i][j]*C(i+k,i)%mod*C(k,l));
    				}
    			}
    		}
    	}
    	printf("%lld
    ",f[n][0]);
    	
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    HDU 多校对抗赛第二场 1010 Swaps and Inversions
    Android Tab -- 使用ViewPager、PagerTitleStrip/PagerTabStrip来实现
    Android Tab -- 使用ViewPager、Fragment、FragmentPagerAdapter来实现
    Android Tab -- 使用Fragment、FragmentManager来实现
    Android Tab -- 使用ViewPager、PagerAdapter来实现
    Android Tab -- 使用TabWidget、TabHost、TabActivity来实现
    Android Programming: Pushing the Limits -- Chapter 6: Services and Background Tasks
    Android Programming: Pushing the Limits -- Chapter 5: Android User Interface Operations
    Android Programming: Pushing the Limits -- Chapter 4: Android User Experience and Interface Design
    Android Programming: Pushing the Limits -- Chapter 3: Components, Manifests, and Resources
  • 原文地址:https://www.cnblogs.com/gmh77/p/13736530.html
Copyright © 2011-2022 走看看