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 ...
#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 模拟..
#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左右了.
#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; }