zoukankan      html  css  js  c++  java
  • bzoj 2004: [Hnoi2010]Bus 公交线路

    Description
    小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距
    离均为1km。 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计线路:
    1.设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站。
    2.每个车站必须被一辆且仅一辆公交车经过(始发站和
    终点站也算被经过)。
    3.公交车只能从编号较小的站台驶往编号较大的站台。
    4.一辆公交车经过的相邻两个
    站台间距离不得超过Pkm。 在最终设计线路之前,小Z想知道有多少种满足要求的方案。由于答案可能很大,你只
    需求出答案对30031取模的结果。

    解题报告:
    用时:2h30min,1AC
    这题拿着毫无办法,只能请教大佬,原来是矩阵优化DP,这个DP也是非常的诡异,首先我们要保证移动不超过P,所以我们以P为单位做矩阵,然后P很小可以状压,所以我们想办法DP,可以设(f[i][j])表示(i)这个位置起,后面P个站的是否有车的状态,1表示有,0表示没有,转移方程类似于:
    (f[i][j]+=f[i-1][k]) 条件是(j)相比(k)只动了一辆车的先后顺序。
    但是没这么简单,因为对于((1,3),(2,4))这两个状态既可以从((1,3),(2))转移来,也可以从((1),(2,4))转移来,所以方案会算重,所以要强制要求只有一辆车可以动,这里强制只能动 (i),也就是说这个例子中只有 (1) 可以移动,就没问题了,因为每次只移动一位且转移相同,直接丢到矩阵里做n-k次即可,然后辅助矩阵的((i,j))表示i这个状态可以转移到j,所以合法的话我们就赋值为1,对于(i)是否能转移到(j)(check),就是相当于(i)整体右移一位,并且除了第P位,其他位置要相同,只有多出来的位置可以不同.

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    const int mod=30031;
    int n,m,p,f[233],cnt=0;
    void dfs(int dep,int tot,int sum){
    	if(sum==m){
    		f[++cnt]=tot;
    		return ;
    	}
    	if(dep==p+1)return ;
    	dfs(dep+1,tot+(1<<(p-dep)),sum+1);
    	dfs(dep+1,tot,sum);
    }
    struct mat{
    	int a[141][141];
    	mat(){memset(a,0,sizeof(a));}
    	mat operator *(const mat &pr)const{
    		mat tmp;
    		for(int i=1;i<=cnt;i++)
    			for(int j=1;j<=cnt;j++)
    				for(int k=1;k<=cnt;k++){
    					tmp.a[i][j]+=a[i][k]*pr.a[k][j]%mod;
    					if(tmp.a[i][j]>=mod)tmp.a[i][j]-=mod;
    				}
    		return tmp;
    	}
    };
    bool check(int x,int y){
    	int tmp=(x-(1<<(p-1)))<<1;
    	y^=tmp;if(y==(y&(-y)))return true;
    	return false;
    }
    void work()
    {
    	scanf("%d%d%d",&n,&m,&p);
    	dfs(2,1<<(p-1),1);
    	mat T;
    	for(int i=1;i<=cnt;i++)
    		for(int j=1;j<=cnt;j++)
    			if(check(f[i],f[j]))T.a[i][j]=1;
       mat S;
    	for(int i=1;i<=cnt;i++)S.a[i][i]=1;
    	n-=m;
    	while(n){
    		if(n&1)S=S*T;
    		T=T*T;n>>=1;
    	}
    	printf("%d
    ",S.a[1][1]);
    	//为什么答案是这个?因为我的dfs顺序是这样的啊=.=
    }
    int main()
    {
    	work();
    	return 0;
    }
    
    
    
  • 相关阅读:
    [angularjs] angularjs系列笔记(五)Service
    [android] 隐式意图的配置
    [android] 隐式意图激活另外一个activity
    [angularjs] angularjs系列笔记(四)过滤器
    [android] 显示意图激活另外一个activity
    [android] smartimageview&常见的开源代码
    [angularjs] angularjs系列笔记(四)控制器
    [android] 上传文件到服务器
    [android] 异步http框架与实现原理
    [android] 采用httpclient提交数据到服务器
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7587307.html
Copyright © 2011-2022 走看看