zoukankan      html  css  js  c++  java
  • [JXOI2012]奇怪的道路

    XXXVII.[JXOI2012]奇怪的道路

    神题。

    (为以示区别,题面中的\(k\)我们称作\(p\))。

    思路1.

    观察到\(k\)很小,考虑状压。

    \(f[i][j][k]\)表示:

    \(i\)个位置的边已经全部连完了,位置\([i-p+1,i]\)的状态压起来是\(j\),并且连了\(k\)条边的方案数。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    int f[50][1<<10][50],n,m,lim,p;
    int ksm(int x,int y){
    	int z=1;
    	for(;y;x=(1ll*x*x)%mod,y>>=1)if(y&1)z=(1ll*z*x)%mod;
    	return z;
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&p),lim=1<<p;
    	f[0][0][0]=1;
    	for(int i=0;i<n;i++)for(int j=0;j<min(1<<i,lim);j++)for(int k=0;k<=m;k++){
    		if(!f[i][j][k])continue;
    		for(int g=0;g<min(1<<(i+1),lim);g++){
    			int diff=((g>>1)^j);
    			if(__builtin_parity(diff)!=(g&1))continue;
    			int cnt=__builtin_popcount(diff);
    			for(int h=k+cnt;h<=m;h+=2)(f[i+1][g][h]+=1ll*f[i][j][k]*ksm(min(i,p),(h-k-cnt)>>1)%mod)%=mod;
    		}
    	}
    //	for(int i=1;i<=n;i++)for(int j=0;j<min(1<<i,lim);j++)for(int k=0;k<=m;k++)printf("%d,%d,%d:%d\n",i,j,k,f[i][j][k]);
    	printf("%d\n",f[n][0][m]);
    	return 0;
    }
    

    一交,WA,\(45\%\)

    咋肥事?

    因为这么连,会有重复计算的部分(因为边是无序的,同一组边集只不过因为顺序不同就会加不止一次)。

    思路2.

    为了凸显顺序,我们不得不考虑再增加一维。

    \(f[i][j][k][l]\)表示:

    \(i\)个位置的边已经全部连完了,连了\(k\)条边,位置\([i-p+1,i+1]\)的状态压起来是\(j\),并且位置\(i+1\)只与\([i-p+1,i-l]\)里的点连了边的方案数。

    显然,初始\(f[1][0][0][0]=1\),答案是\(f[n][m][0][0]\)

    考虑如何转移(刷表法)。

    1. 我们再连一条边\((i-l-1,i+1)\)

    \(f[i][j+1][k\operatorname{xor}2^0\operatorname{xor}2^l][l]+=f[i][j][k][l]\)

    1. 连接\((i-l-1,i+1)\)之间的边已经全部连完,来到下一位。

    \(f[i][j][k][l+1]+=f[i][j][k][l]\)

    1. \(l\)枚举完成后,

    如果有\(k\)的第\(p\)位为\(0\),则可以转移到下一位,则有

    \(d[i+1][j][k<<1][\min(i,p)]+=f[i][j][k][0]\)

    复杂度\(O(nmp2^p)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    int n,m,p,f[40][40][1<<10][10],lim;
    int main(){
    	scanf("%d%d%d",&n,&m,&p),lim=(1<<(p+1));
    	f[1][0][0][0]=1;
    	for(int i=1;i<=n;i++)for(int j=0;j<=m;j++)for(int k=0;k<lim;k++){
    		for(int l=min(i-1,p);l;l--){
    			if(j<m)(f[i][j+1][k^(1<<l)^1][l]+=f[i][j][k][l])%=mod;
    			(f[i][j][k][l-1]+=f[i][j][k][l])%=mod;	
    		}
    		if(!(k&(1<<p)))(f[i+1][j][k<<1][min(i,p)]+=f[i][j][k][0])%=mod;
    	}
    	printf("%d\n",f[n][m][0][0]);
    	return 0;
    }
    

  • 相关阅读:
    随意给一组数,找出满足一下条件的a[i],a[i]左边的数小于等于a[i],a[i]右边的数大于等于a[i]
    SVN
    四种进程或线程同步互斥的控制方法
    二叉树转双向链表
    最大连续子序列和
    找出一个字符串中第一个只出现一次的字符
    清除浮动的那些事
    jQuery中ready与load事件的区别
    css 经典布局之圣杯布局(左右固定,中间自适应)
    window.name + iframe跨域实例
  • 原文地址:https://www.cnblogs.com/Troverld/p/14597187.html
Copyright © 2011-2022 走看看