FOJ月赛-2013年4月(校赛热身赛)

  A Jason的特殊爱好

    组合数学, 通过计算 [1,n]区间数量解决.

    假设n十进制表示位数为L,则通过统计长度为 L, L-1, ..., 1的数量来得出结果.    

    假设 n = a1,a2,a3,...,aL    // 十进制表示,其中a1为高位

    一.当长度小于L, 假设其为 len = 1,2,...,L-1

      则当前数 X = a1,a2,a3,...,a_len

      因为 Length(X) < Length(N), 所以任意的X都小于N,

      我们可以枚举 X中包含的1的个数,假设其为 K, 则 K = 0,1,2,...,len

      这里考虑, 因为高位不能为0,而其它位可以为 0.

      这里分两种情况:

        1. a_1 为1,  则 a_2,..,a_len ,中有 k-1位取1, 有 n-k位不取1,

对于不取1的位置,其可以取(0,2,3,..,9) ,所以方案数为 C(n-1,k-1) * 9 ^ (n-k).

        1. a_1 不为1, 则 a_2,...,a_len,中有 k个为取1, 有n-k位不取1,

对于不取1的位置, 若是a_1位置则只能取(2,3,..,9)共8种,而a_i ( i != 1 ) 则可以取( 0,2,..,9 )共9种,所以方案数位 8*9^(n-1-k)*C(n-1,k) 

      而 k 的取值范围为 (0,1,2,..,len ) 

      则总和为

              注意 k = len 则要单独计算因为没有 a_1不为1的情况。

    二.当长度等于L

      若当前数 X = a1,a2,a3,...,a_L

      我们则一位一位处理, 当我们处理当 第i位, 则 (1,i-1)位 为1的数量为 cnt

      若 a_i < 0 , 则当前数 X 要小于 N, 则当前位只能取0. 

      若 a_i = 1 , 则当前数 X 要小于 N, 则当前位可以取0

          则 X' = a_1,a_2,...,a_i = 0, a_(i+1)..., a_L  

          此时, 对于 a_(i+1) ,..., a_L 位(共有L-i位)而言,取任意数都 会使 X' < N , 意味着都满足要求.

          则我们可以通过枚举 此区间1的个数, 假设其为 k = 0,1,2,.., L-i 

          则方案数为   (cnt+k)*C(L-i, k)*9^(L-i-k) 

      若 a_i > 1 , 则当前数 X 要小于 N, 则当前位可以取 ( 0,1,2, ..., (a_i-1) )

          这里其实和 a_i = 1 计算方式差不多, 只是要注意.

          若我们取 a_i = 1, 则 1的位数统计的时候就要多一个 即 cnt+1+k

          对于 a_i 不取 1, 则为  cnt+k ...

View Code
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<map>
#include<string>
#include<cmath>
using namespace std;

typedef unsigned long long LL;

LL C[20][20], fact[20];
void init(){
    for(int i = 0; i <= 18; i++)
        C[i][0] = C[i][i] = 1;
    for(int i = 2; i <= 18; i++ ){
        for(int j = 1; j < i; j++)
            C[i][j] = C[i-1][j-1] + C[i-1][j];    
    }    
    fact[0] = 1;
    for(int i = 1; i <= 18; i++) fact[i] = fact[i-1]*9;
}
LL sum( LL x ){
    LL res = 0;
    if( x <= 0 ) return res; 
    int a[20], n = 0;
    LL t = x; while( t ){ t/=10; n++; }
    t = x;
    for(int i = n; i >= 1; i-- ){
        a[i] = t%10; t /= 10;
    }
//    printf("n = %d\n", n );
//    for(int i = 1; i <= n; i++){
//        printf("%d ", a[i] );    
//    }
//    puts("");
    for(int L = 1; L <= n-1; L++ ){
        res += L;
        for(int k = 1; k < L; k++){
            res += 1LL*k*( fact[L-k]*C[L-1][k-1] + 8LL*fact[L-1-k]*C[L-1][k] );
        }
    }
//    printf("res = %I64u\n", res ); //Accepted
    int c = 0;
    LL tmp = res;
    for(int i = 1; i <= n; i++){
        if( a[i] == 1 ){
            if( i > 1 ){
                LL tmp = 0;
                for(int j = 0; j <= n-i; j++){
                    tmp += (c+j)*( C[n-i][j]*fact[n-i-j] );     
                }   
                res += tmp;
            }
            c++;    
        }
        else if( a[i] > 1 ){
            LL tmp = 0;
            for(int j = 0; j <= n-i; j++){
                tmp += (c+1+j)*( C[n-i][j]*fact[n-i-j] );    
                if( i == 1 ) tmp += (a[i]-2)*(c+j)*( C[n-i][j]*fact[n-i-j] );
                else tmp += (a[i]-1)*(c+j)*( C[n-i][j]*fact[n-i-j] );
                
            }  
        //    printf("tmp = %I64u\n", tmp );
            res += tmp;
        }    
    } 
//    printf("add = %I64u\n", res+c - tmp ); // Wrong Answer  1248
    return res + c;
}
int main(){
//    freopen("test.out","w",stdout);
    init();
//    printf("%I64u\n", sum( 1951 ) );
//    for(LL b = 1; b <= 99999; b++ ){
    LL a , b;
    while( scanf("%I64u%I64u", &a,&b) != EOF ){  
        if( a > b ) swap( a, b );
        printf("%I64u\n", sum(b) - sum(a-1) );
//        if( b == 1951)    printf("sum(1951)=%I64u\n", sum(1951) );
//        if( sum(b) == 1416 ) printf("b = %I64u\n", b );
//    }
    }
    return 0;    
}

  C 模拟..

View Code
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <algorithm>
using namespace std;
int T, N, x; 
int gcd( int x, int y )
{
    return y==0?x:gcd( y, x%y);
}
struct Node
{
    int x, y, z;
    bool operator < ( const Node &a )const{
        return z>a.z;    
    }
}ans[15]; 
bool vis[15];

int main( )
{ 
    scanf( "%d", &T);
    while( T -- ){
        scanf( "%d", &N );
        memset( vis, 0, sizeof(vis));
        int n = 0, idx = 0;
        for( int i=0; i<N; ++ i ){
            scanf("%d", &x );
            if( x != 0 ){
                ans[n++].x=x;
                vis[i] = 1;
            }
        }    
        for( int i=0; i<N; ++ i ){
            scanf( "%d", &x );
            if( vis[i] == 1 ){
                ans[idx].y=x+1;
                int t= gcd( abs(ans[idx].x), abs(ans[idx].y) );
                ans[idx].x/= abs(t);
                ans[idx].y/= abs(t); 
            //printf( "~~~~~~ %d %d\n", ans[i].x, ans[i].y ); 
                ans[idx++].z=x+1;
            }
        }
        
        
        sort( ans, ans+n );
        if( abs(ans[0].y)>1 )
            printf( "%d%/%d", ans[0].x, ans[0].y );
        else if( abs(ans[0].x)>1 ) printf( "%d", ans[0].x );
        else if( abs(ans[0].x) == 1 && ans[0].x < 0 ) printf( "-" );
        if( ans[0].z>0 ){
            if( ans[0].z>1 ){
                printf( "x^%d", ans[0].z );
            }
            else printf( "x" ); 
        }
        for( int i=1; i<n; ++ i ){
            if( abs(ans[i].y)>1 )    printf( "%+d%/%d", ans[i].x, ans[i].y );
            else if( abs(ans[i].x)>1 )printf( "%+d", ans[i].x );
            else if( abs(ans[i].x)==1 )printf( ans[i].x==1?"+":"-" ); 
            if( ans[i].z>0 ){
                if( ans[i].z>1 )
                    printf( "x^%d", ans[i].z );
                else printf( "x" ); 
            }
                
        }
        puts( "" ); 
        
    }
    return 0;
}

 

  H DFS减枝..

    因为 9! 等价 1e6 在算上比较, 1e7 算法还是能够跑的..处理上其实可以优化掉  10次左右的比较 

    时间复杂度就为 1e6左右了.

View Code
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MIN(a,b) (a)<(b)?(a):(b)
const int inf = 0x3fffffff;
 
int g[15][15], a[15], n, len;
int ans;
int b[15];

bool vis[10];

void dfs( int num, int sum ){
    if( sum > ans ) return;
    if( num == len ){ 
        if( sum < ans ) ans = sum;
        return;
    }    
    for(int i = 0; i < len; i++){
        if( !vis[i]  ){ 
            if( num == 0 ){
                if( a[i] == 0 ) continue;
                vis[i] = 1;
                b[num] = a[i];
                dfs( num+1, 0 );
                vis[i] = 0;
            }
            else{ // num > 0 
                if( sum + g[a[i]][b[num-1]] < ans ){
                    vis[i] = 1;
                    b[num] = a[i];
                    dfs( num+1, sum+g[a[i]][b[num-1]] );
                    vis[i] = 0;    
                }
            }
        }    
    }
} 
int main(){
    int T, X;
    scanf("%d", &T);
    while( T-- ){
        scanf("%d", &X );
        for(int i = 0; i < 10; i++)
            for(int j = 0; j < 10; j++ )
                scanf("%d", &g[i][j] );
        ans = inf;len = 0;
        int t = X;
        
        while( t ){
            a[len++] = t%10;
            t /= 10;    
        }
        memset( vis, 0, sizeof(vis) );
        dfs( 0, 0 );
        printf("%d\n", ans );
    }    
    return 0;    
}

 

posted @ 2013-04-06 18:32  yefeng1627  阅读(281)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor