zoukankan      html  css  js  c++  java
  • [FJOI2016] 建筑师

    题目描述

    小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 nn 个建筑,每个建筑的高度是 11 到 nn 之间的一个整数。

    小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同。另外小 Z 觉得如果从最左边(所有建筑都在右边)看能看到 AA个建筑,从最右边(所有建筑都在左边)看能看到 BB 个建筑,这样的建筑群有着独特的美感。现在,小 Z 想知道满足上述所有条件的建筑方案有多少种?

    如果建筑 ii 的左(右)边没有任何建造比它高,则建筑 ii 可以从左(右)边看到。两种方案不同,当且仅当存在某个建筑在两种方案下的高度不同。

    输入输出格式

    输入格式:

    第一行一个整数 TT ,代表 TT 组数据。 接下来 TT 行,每行三个整数 n,A,Bn,A,B 。

    输出格式:

    对于每组数据输出一行答案  ext{mod } 10^9+7mod 109+7 。

    输入输出样例

    输入样例#1: 
    2
    3 2 2
    3 1 2
    输出样例#1: 
    2
    1

    说明

    对于 10 \%10% 的数据 : 1 leq n leq 101n10 。

    对于 20 \%20% 的数据 : 1 leq n leq 1001n100 。

    对于 40 \%40% 的数据 : 1 leq n leq 50000, 1 leq T leq 51n50000, 1T5 。

    对于 100 \%100% 的数据 : 1 leq n leq 50000, 1 leq A, B leq 100, 1 leq T leq 2000001n50000, 1A,B100, 1T200000 。

        乍一看这不是以前在UVA做过的一道题么。。。。只不过数据范围无限扩大。。。。

        一开始想看看能不能卡常卡过去。。。。于是就把那个题又写了一遍。。。但是跑了40+s才能跑出最大的数据。。。。

        虽然很慢,但是 最基本的dp方程 也在这个程序里出现了,之后对程序的优化也需要用到这个转移的性质,所以还是先贴一下:

    /*
        f[i][j][k] -> f[i+1][j+1][k]
        f[i][j][k] -> f[i+1][j][k+1]
        f[i][j][k] * (i-1) -> f[i+1][j][k]
    */
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    #define pb push_back
    const int ha=1e9+7;
    inline void add(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
    struct node{ int num,A,B;};
    vector<node> g[200005];
    int f[2][105][105],T,n,X;
    int Y,ans[200005],M,a,b;
    
    inline void solve(){
    	f[0][1][1]=1;
    	for(int i=2,now=1,pre=0;i<=M;pre=now,now^=1,i++){
    		memset(f[now],0,sizeof(f[now]));
    		
    		for(int j=0;j<=a;j++)
    		    for(int k=0;k<=b;k++) if(f[pre][j][k]){
    		    	add(f[now][j][k],f[pre][j][k]*(ll)(i-2)%ha);
    		    	add(f[now][j+1][k],f[pre][j][k]);
    		    	add(f[now][j][k+1],f[pre][j][k]);
    			}
    		
    		node x;
    		
    		for(int j=g[i].size()-1;j>=0;j--){
    			x=g[i][j];
    			ans[x.num]=f[now][x.A][x.B];
    		}
    	}
    }
    
    int main(){
    	scanf("%d",&T);
    	for(int i=1;i<=T;i++){
    		scanf("%d%d%d",&n,&X,&Y);
    		g[n].pb((node){i,X,Y});
    		M=max(M,n),a=max(a,X),b=max(b,Y);
    	}
    	
    	solve();
    	
    	for(int i=1;i<=T;i++) printf("%d
    ",ans[i]);
    	return 0;
    }
    

      但显然正解只能允许 50000 * 100的啦。。。。

          我们考虑一下dp方程的转移,对于后两维,每次要么都不变(然后都乘上一个系数),或者两维之一+1。

         所以怎么快速计算这个呢?? 我们考虑把后两位合并,最后再考虑两维+1顺序的问题。。。

        也就是设 f[i][j] 为已经放了 i个桩子,左+右一共能看见j个的方案数(无序的)。

        最后询问 A,B的时候 再乘上C(A+B-2,A-1)  [枚举两维+1的顺序] 就行啦。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int ha=1e9+7;
    inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
    inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
    int f[50005][233],C[233][233],T,n,A,B;
    
    inline void init(){
    	C[0][0]=1;
    	for(int i=1;i<=200;i++){
    		C[i][0]=1;
    		for(int j=1;j<=i;j++) C[i][j]=add(C[i-1][j-1],C[i-1][j]);
    	}
    	
    	f[1][2]=1;
    	for(int i=1;i<50000;i++)
    	    for(int j=2;j<=200;j++) if(f[i][j]){
    	    	ADD(f[i+1][j],f[i][j]*(ll)(i-1)%ha);
    	    	ADD(f[i+1][j+1],f[i][j]);
    		}
    }
    
    int main(){
    	init(),scanf("%d",&T);
    	
    	while(T--){
    		scanf("%d%d%d",&n,&A,&B);
    		printf("%d
    ",f[n][A+B]*(ll)C[A+B-2][A-1]%ha);
    	}
    	
    	return 0;
    }
    

      

  • 相关阅读:
    hibernate自动建表时设置编码格式
    【友盟统计报表解读】之错误分析iOS版
    用vs2008和vs2005创建win32 console application
    win7 无法启动此程序,因为计算机中丢失glut32.dll
    visual studio 2005 win7 64位版下载
    win7兼容visual studio 2005 的方法
    OpenGL入门学习(一)(转)--环境搭建
    opengl教程
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)用法
    OpenGL函数思考-glColor
  • 原文地址:https://www.cnblogs.com/JYYHH/p/9048111.html
Copyright © 2011-2022 走看看