zoukankan      html  css  js  c++  java
  • AOAPC I: Beginning Algorithm Contests (Rujia Liu) Volume 6. Mathematical Concepts and Methods

    uva 106 这题说的是 说计算 x^2 + y^2  =  z^2  xyz 互质 然后计算个数和 在 N内 不在 勾股数之内的数的个数 然后去找需要的 

    维基百科上 看到 另 n*m*2 =b     n*n+m*m=c  m*m-n*n = a  m>n 然后这样可以计算出 所有互质的 勾股数 然后进行枚举n和m 进行判断 还有一个问题他为什么不会遗漏 互质的勾股数呢   我自己想了一下 应该是这样的  也就是说 n^2 和 m^2  必须为整数 就是说 他们是分数已经不可能了 那现在来讨论 他们是带根号的时候的数 也就是说 存在这样的数 那么当n*m 必须为整数 可以知道 如 果 有 一 个 为 根 号 那 么 必 须 两 个 都 带 有 那么可以知道他们最简根式根号内的东西必须是一样的 那么 如果一样就导致了 b 和 c 具 有 一 样 的公约数 然后就不成立了  那么 n和m 必须为 整数

    若需要一组最小数为奇数的勾股数,可“任意选取”一个大于1的奇数,也就是3或以上的奇数,将该数自乘为平方数,除以2,答案加减0.5可得到两个新的数字,这两个数字连同一开始选取的奇数,三者必定形成一组勾股数。但却不一定是以这个选取数字为起首勾股数的唯一可能,例如(27,364,365)并非是以27为起首的唯一勾股数,因为存在另一个勾股数是(27,36,45),同样也以27为首。

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    const int maxn = 1000005;
    int es[1005],N;
    bool mark[maxn];
    int gcd(int a, int b){
        return b==0?a:gcd(b,a%b);
    }
    void jud(int x,int y,int z){
    
        for(int i = 1 ; i <=N ; i ++){
            if(i*z>N)break;
            mark[i*x] = true;
            mark[i*y] = true;
            mark[i*z] =true;
        }
    }
    int main()
    {
        for(int i = 1; i <= 1000 ; ++i)
             es[i] =  i*i;
        while(scanf("%d",&N) == 1){
            for(int i = 0 ; i <= N ; ++ i )
                mark[i] = false;
            int ans = 0,p=0,T=sqrt(N+0.5);
            for(int n = 1 ; n<=T ; n++){
                 for( int m =n+1 ; m<=T ; m ++){
                     if(es[n]+es[m] > N) break;
                     if(gcd(n,m) != 1|| (n%2&&m%2)) continue;
                     int x= es[m]-es[n];
                     int y= 2*m*n;
                     int z= es[m] +es[n];
                        ans ++;
                        jud(x,y,z);
                     }
                 }
    
    
            for(int i = 1 ; i <= N ; ++ i) if(mark[i]==false) p++;
            printf("%d %d
    ",ans,p);
    
        }
        return 0;
    }
    View Code

    uva 10673 这题说的是 就是算出使得 X/K 向下取整 和X/K向上取整 发现当能整除的时候直接输出 0 K 否则输出 -X 和 X 因为他们只差1

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    typedef long long LL;
    int main()
    {
         int t;
         scanf("%d",&t);
         while(t -- ){
            long long  X,K;
            cin>>X>>K;
             if(X%K == 0){
    
                 cout<<0<<' '<<K<<endl; continue;
             }
              cout<<-X<<' '<<X<<endl;
         }
    
        return 0;
    }
    View Code

     uva11121 这题说的是给了一个数 叫你将他改写为 -2 进制的数  可以知道 当数字是大于0 的时候 位置在 奇数位的时候 2^k   =  2^(k+1)  - 2 ^(k) 次方  然后就会得到 每个位置上 的个数 可以知道 (-2)^k   (-2)^(k+2) +(- 2)^(k+1) 然后 这样进行转化 得到    到最后 可会出现循环  循环的原因是 2*(-2)^k  == (-2)*(k+1)  对于这样的数可以削去 然后就得到了

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    int num[150];
    int  jud(int cas,int t, int n){
          int loc = 0;
          memset(num,0,sizeof(num));
          while( n ){
              if( n%2 == 1 ){
                 if(loc%2!=t)
                       ++num[loc+1];
    
                     ++num[loc];
              }
             loc++;
             n=n/2;
          }
          for(int i =0 ; i <150-2 ; i ++)
          while(num[i]>1){
              num[i]-=2;
              num[i+1]++; num[i+2]++;
          }
            printf("Case #%d: ",cas);
          for(int i = 100-2 ; i >0 ;i --){
                 if(num[i]!=0){
                     for( int j = i ; j >0 ; j --)
                       printf("%d",num[j]);
                   break;
                 }
    
          }
          printf("%d
    ",num[0]);
         return 0;
    }
    int main()
    {
         int t;
         scanf("%d",&t);
        for(int cas =1 ; cas <= t; ++ cas ){
               int  n ;
               scanf("%d",&n);
               if(n>=0){
                jud(cas,0,n);
               }
               else{
                   jud(cas,1,-n);
               }
         }
    
        return 0;
    }
    View Code

     uva10791 这题说的是给了 一个数字 然后让你计算出 大于等于 两个 数的 最小公倍数是 N 然后使得 这些数字的和最小   我们可以这么想这些数字能组成 最小公倍数是 n 应该使得这些数之间的 最大公约数为 1 这样或许可以减少一些值 然后 我们可以想象 一下 如果 最小公倍数 可以用 2 个数字 表示也可以 用3 个数字 表示 那么这显然是用3个数字表示和值会比较小,因为他们那个毕竟是加 和乘的差别 然后就得到了解在边界问题上wa 了一次 然后 改了一下 T 了 范围改小点就A 了

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    const int inf = 0x7fffffff;
    int prime[10000];
    bool vis[100000];
    int main()
    {
    
          memset(vis,false,sizeof(vis));
            long long N = 50000;
            long long num = 0;
            for(long long  i = 2 ; i<=N; i ++)
            if(vis[i] == false){
                  prime[num++] = i;
                  for(long long  j = i*i ; j <= N ; j+=i )vis[j] = true;
            }
            long long n,cas =0;
    
            while(true){
                scanf("%lld",&n);
                if(n == 0 ) break;
                long long loc = 0,ans =0,ge=0;
                while(loc<num &&n>1){
                     bool fal = false;
                    int t=1;
                    while(n%prime[loc] == 0){
                         t =t *prime[loc];
                         n = n / prime[loc];
                         fal = true;
                    }
                    if(fal) {
                            ans += t;
                             ge ++;
                    }
                    loc++;
                }
                if( ge == 0 ){
                    ans = n+1;
                }
                else if(ge==1 && n==1) ans += 1;
                else if(n!=1) ans +=n;
                 printf("Case %lld: %lld
    ",++cas,ans);
            }
           return 0;
    }
    View Code

     uva 10717 这题说的是 给了很多种硬币厚度 每张桌子有 4 条腿 每条腿 只能用一种 硬币 一张桌子必须用 4种硬币 然后枚举每每四种硬币  求最小公倍数

    #include <iostream>
    #include <string.h>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int maxn = 3000000;
    const long long inf =1000000000000000000;
    long long  hight[55];
    long long to[maxn];
    long long gcd(long long  a,long long b){
         return b== 0?a:gcd(b,a%b);
    }
    long long minv(long long a, long long b){
       return a>b?b:a;
    }
    int main()
    {
         int n,tt,num;
         while(scanf("%d%d",&n,&tt) == 2){
                if(n == 0 && tt  ==  0 ) break;
            int r =0;
           for(int i = 0 ; i < n ; ++ i)
            {
                cin>>hight[r];
                if(hight[r] == 0 )continue;
                r++;
            }
            n =r;
            num =0;
            for(int i = 0 ; i < n-3 ; ++ i )
                for(int j = i+1 ; j < n-2 ; ++ j){
    
                        long long y1 = gcd(hight[i] , hight[j]);
    
                        long long ans1 = hight[i] * hight[j] / y1;
    
                        for(int k = j+1 ;k <n - 1; ++ k){
    
                            long long y2 = gcd(ans1, hight[k]);
    
                            long long ans2 = hight[k] * ans1 /y2;
    
                            for(int t = k +1 ; t < n ; ++t ){
    
                               long long y3 = gcd(ans2, hight[t]);
    
                               long long  ans3 = hight[t] * ans2 /y3;
    
                               to[ num ++ ] =ans3;
                            }
                        }
                    }
    
    
    
            for(int i = 0 ; i < tt ; ++ i){
                long long ans;
                cin>>ans;
                long long L = inf , R = inf;
    
                for(int j = 0 ; j < num ; ++ j){
                    long long t = ans % to[j];
                    if(ans <= to[j]){
                        L = minv( L , ans);
                        R = minv(R , to[j] - ans);
                    }
                    else {
                        L = minv(L,t);
                        R =minv(R,(to[j]-t)%to[j]);
                    }
                    if(L == 0 && R == 0) break;
                }
                cout<<ans - L<<' '<<ans + R<<endl;
            }
         }
         return 0;
    }
    View Code

     uva 11027 这 题 说 的 是 给 了 一 个 字 符 串 输 出 回 文 串 输 出 第 n 大 的 回 文 字 符 串 的 刚开想 通过数学类似康托展开的东西去 映射 发现不行 毕竟不支持有重点的,还是没想法 看了解题报告后才知道用枚举每一位的逼近所要的数值 尽量取字典序小的放在前面如果 方案数小于 n 就用 n-num 否则就 直接下一个位忘了可以用这种方法解决 嗨思维需要活跃一点啊啊啊啊啊

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <vector>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    LL f[16],n,len,add;
    char str[30],output[30],cap;
    int num[26],number[26];
    vector<char>t;
    LL jud(int nn, int tr){
       LL add = 1;
       num[tr]--;
       for(int i = 0 ; i < 26 ; i++)
         add*=f[num[i]];
       num[tr]++;
       return f[nn]/add;
    }
    void solve(int L){
         int ge = 0;
         t.clear();
         for(int i = 0 ; i<26 ; ++ i){
              for(int j = 0 ; j<num[i] ; ++ j)
                  t.push_back('a'+i);
         }
         sort(t.begin(),t.end());
         LL sum;
         for(int i =0;  i<L; ++ i ){
            int tr;
             for(tr = 0 ; tr<t.size() ; ++ tr){
                 if(tr!=0&&t[tr-1] == t[tr] ) continue;
                sum = jud(L-1-i,t[tr]-'a');
                if(sum>=n)break;
                n-=sum;
             }
             output[ge++]=t[tr];
             num[t[tr]-'a']--;
             t.erase(t.begin()+tr);
         }
         if(len%2)output[ge++]= cap;
         for(int i = 0 ; i<L ; ++ i)
             output[ge++] = output[L-1-i];
    }
    int main()
    {
         f[0] = 1;
        for(int i = 1 ; i<=15; ++ i)
             f[i]=f[i-1]*i;
        int t;
        scanf("%d",&t);
        for(int cas = 1 ;cas <= t ;cas ++ ){
            scanf("%s%lld",str,&n);
            memset(num,0,sizeof(num));
            memset(number,0,sizeof(number));
            len  = strlen(str);
            for(int i = 0 ; i<len; ++ i)
                 num[str[i]-'a']++;
            int oddNum = 0;
            for(int i = 0 ; i<26 ; ++ i ){
                number[i] = num[i];
                if(num[i]%2) {++oddNum ;  cap = 'a' +i ;}
                num[i]=num[i]/2;
            }
            if(oddNum>1){ printf("Case %d: XXX
    ",cas); continue;}
            int L = len / 2 ;
            add =1;
            LL permutationNum = f[L];
            for(int i = 0 ; i < 26 ; ++ i)
                if(num[i]>0)
                  {permutationNum /=f[ num[ i ] ]; add*=f[num[i]];}
            if( n > permutationNum ){
                printf("Case %d: XXX
    ",cas); continue;
            }
            solve(L);
                 output[ len ] = 0;
                printf("Case %d: %s
    ",cas,output);
        }
        return 0;
    }
    View Code

     uva 10820 这题说的是 给了一个数N 这个 N是大于1 小于50001的数 然后计算出 使得在小于等于N的 数 中 有对少 对互质的数,(1,2)和(2,1)算不同,  用白书上的欧拉函数解决就好了 ,欧拉函数计算小于等于n的 与n互质的个数,将结果乘以2 加一起就 好了 得去了解一下中国剩余定理了

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn =50001;
    long long num[maxn];
    long long ans[maxn];
    void intial(){
        memset(ans,0,sizeof(ans));
        memset(num,0,sizeof(num));
        ans[1]=num[1] = 1;
        for(int i = 2 ; i < maxn ; ++ i){
             if(num[i]==0){
                for(int j = i ; j <= maxn ; j += i){
                    if(num[j]== 0) num[j] = j;
                    num[j]= num[j]/i*( i - 1 );
                    }
            }
             ans[i]+=ans[i-1]+num[i]*2;
        }
    }
    
    int main()
    {
           intial();
             while(true){
                  int n;
                  scanf("%d",&n);
                  if(n == 0) break;
                  printf("%lld
    ",ans[n]);
             }
            return 0;
    }
    View Code

     uva 5 7 1 这题说的是 给了 两个杯子和一个体积 这两个 杯子 分别 是A 和 B ,B的体积大于A的体积,给了 一个体积 假设为 N ,然后 最后使得 B 中 拥有N 体积的水 N<B<100, A与B 互质, 这样就使得,A 和 B 可以拼得到任意体积的水(小于B的) 然后因为可以列的这样的一个方程(Ax+By=N(呵呵 x不断的在增加))这样就得到了,应该要求的 将 A 中的 水不断的往B中倒知道满足N体积的水。

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn =50001;
    int A,B,N,x,y,gcd;
    int main()
    {
             int inA,inB;
             while( scanf("%d%d%d",&A,&B,&N) == 3 ){
                    inA = 0;
                    inB = 0;
                    while(true){
                          if( inB == N ){   break;  }
                          if(inA == 0){
                             puts("fill A"); inA+=A;
                          }else if(inA < A){
                             puts("pour A B"); inB = inA; inA= 0;
                          }
                          else {
                               if( B-inB <= inA){
                                   puts("pour A B");
                                   inA -= B-inB;
                                   inB = B;
                                   if(inB == N) break;
                                   puts("empty B");
                                    inB=0;
                                }
                                else {
                                    puts("pour A B");
                                    inB +=inA;
                                    inA = 0;
    
                                }
                          }
                    }
                  puts("success");
            }
            return 0;
    }
    View Code

     uva 11029 这题说的是 给了一个整数 N 求 N的K次方,N在int 范围内 K 在10000001 计算出该数的前 3 位 和 后 3 位 ,后三位 自然用 快速幂解决 ,前三位需要用到 log 对数,因为N^K次方很大,我们对他求一次 log 就得到了g 然后取整数部分为什么取整数部分就ok了呢 因为10^(G.H)(G为整数部分H为小数部分) = 10^G*10^0.H次方这样就可以知道了G只起到调整小数点位置的作用没有其他的作用这样将他省略了, 答案在乘上一个100就ok了

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    int k_pow(int a,int n){
          int ans = 1 ;
          a=a%1000;
          while(n){
              if(n&1){
                 ans = (ans*a)%1000;
              }
              a=(a*a)%1000;
              n=n/2;
          }
          return ans;
    }
    int main()
    {
         int cas ,A,B;
          scanf("%d",&cas);
            while( cas -- ){
               scanf("%d%d",&A,&B);
               int ans2 = k_pow(A,B);
               double tr = double(B)*log10(A*1.0);
                tr=tr-(int )tr;
                tr=100*pow(10,tr);
                printf("%d...%03d
    ",int(tr),ans2);
            }
            return 0;
    }
    View Code

     uva 10023 这 题 说 的 是 给 了 一 个 10  的 1000 次 方 以 内 的 数 要 对 它 进 行 开 方 自 然 精 确 到 整 数 就 好。 事 先 理 解 一 下 这 个 东 西 ( A B )A 是 10 位 上 的 数 字 B 是 个 位 上 的 数 字 

    (AB)^ 2 = (100*A) + 20 * B * A + B * B , 那么可以知道在这个数字的百位上的数字是应该让A区得最大 且 A*A要小于等于这个数字百位上的数,这样可以让这个数字更接近于开方出来后得到的数字这显然是正确的 这样经给的大整数 从低位到高位进行一次两位两位的分割 然后从最低高位开始不断的去尝试使得最大的B适合这个开方出来的数,因为每次都考虑使得20 * B * A + B * B  最 大的B ,最高位的时候考虑A=0 成功后就将B添加到末尾,可以这样想前面的位置A已经符合了,然后接下来就剩考虑B了 根据这个20 * B * A + B * B  得到最大的B 每次都向后面移两位 不断的去尝试

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    const int maxn = 505 ;
    const int mod = 100 ;
    void swapc(char &a,char &b){ char c; c = a ; a = b ;b = c ;  }
    struct Bignumber{
        int num[maxn],len;
        Bignumber(){
           memset(num,0,sizeof(num));
           len = 1 ;
        }
        void add(Bignumber &A,int &temp){
             while(temp){
                 A.num[A.len++] = temp%mod;
                 temp/=mod;
             }
        }
        void dec(Bignumber &A){
            while(A.num[A.len-1]==0&&A.len>1){
                A.len--;
            }
        }
        Bignumber(char *str){
            memset(num,0,sizeof(num));
            int L = strlen( str );
            for(int i = 0 ; i < L/2 ; ++ i )swapc( str[i] , str[L-i-1] );
            str[L]='0';
            L=( L + 1 )/2;
            len = 0;
            for(int i = 0 ; i  < L*2 ; i += 2  )
               num[ len ++ ]=str[i]-'0'+( str[i+1] - '0' ) * 10;
        }
        Bignumber operator +(int w){
              int temp = w;
              Bignumber ans;
              ans.len = len;
              for(int i = 0 ; i< len ;++ i){
                   int r = num[i] + temp;
                   ans.num[i] = r%mod;
                   temp = r/mod;
              }
              add(ans,temp);dec(ans);
              return ans;
        }
        Bignumber operator +(Bignumber A){
             int L =max(len,A.len),temp =0;
             Bignumber ans;
             ans.len = L;
             for(int i = 0 ; i<L ; ++ i){
                int r = num[i] +A.num[i] +temp;
                ans.num[i] = r%mod;
                temp=r/mod;
             }
              add(ans,temp);dec(ans);
              return ans;
        }
        Bignumber operator *(int w){
    
              int temp = 0;
              Bignumber ans;
              ans.len = len;
              for(int i = 0 ; i < len ; ++ i){
                 int r = num[i]*w +temp;
                 ans.num[i] = r%mod;
                 temp = r / mod;
              }
              add(ans,temp);dec(ans);
              return ans;
        }
        bool operator <(Bignumber A)const {
             if(len!=A.len) return len<A.len?true:false;
             for(int i = len -1 ; i >= 0 ; -- i)
                 if(num[i]!=A.num[i]) return num[i]<A.num[i]?true:false;
            return true;
        }
        Bignumber operator - (Bignumber A){
    
               Bignumber ans;
               ans.len=len;
               for(int i = 0 ; i<len ;++ i)
                   ans.num[i] = num[i] - A.num[i];
               for(int i = 0 ; i <len ; ++ i)
               if(ans.num[i]<0){
                ans.num[i]=ans.num[i]+mod;
                --ans.num[i+1];
               }
              dec(ans);
              return ans;
        }
        void output(){
            printf("%d",num[len -1]);
            for(int i =len -2 ; i>=0 ; -- i)
                 printf("%02d",num[i]);
            printf("
    ");
        }
    };
    char str[maxn*2];
    int main()
    {
           int cas;
          scanf("%d",&cas);
            while( cas -- ){
               scanf("%s",str);
               Bignumber T(str);
               Bignumber ans,cmp;
               Bignumber temp ;
               for(int i = T.len -1 ; i>=0 ; -- i){
                    cmp=cmp*100;
                    cmp =cmp + T.num[i];
                    int b;
                    for( b = 9 ; b>=0 ; b --){
                         temp = ans*(20*b);
                         temp = temp + (b*b);
                        if(temp<cmp) break;
                    }
                    cmp =cmp - temp;
                    ans=ans*10+b;
               }
               ans.output();
               if(cas)puts("");
            }
            return 0;
    }
    View Code

     uva 10308 这题说的是给了一棵树(the roads are built in such a way that there is only one route from a village to a village that does not pass through some other village twice)由这句话可以得到然后要求出树上最远的两点,可以知道树上的一个点到另一个点只有一条道路可以走,这样很显然刚开始不知道怎么搞,发现可以任何选一个点,可以知道他与他最远的孩子距离 ,让他的任意两个孩子与他的距离相加 就得到了这两个孩子的最远距离,这样不断的去取最大值,返回离他最远距离的孩子的距离加上他与父节点的距离

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn=10005;
    class edg{
       public :int to,dist,next;
       edg(int a=0,int b =0 ,int c =0){
           to =a; dist= b; next= c;
       }
    };
    edg E[maxn*2];
    int first[maxn],Num,F[3],ans,S;
    void read(char *str){
         int len = strlen(str),um=0,i=0;
         while(i<len){
            if(str[i]>='0'&&str[i]<='9'){
                 int r=0;
                 while(str[i]>='0'&&str[i]<='9' && i<len){
                     r=r*10+str[i]-'0';
                     ++ i;
                 }
                 F[um++]=r;
            }
            ++i;
         }
         if(S>min(F[0],F[1]))S =min( F[0] , F[1] );
         E[Num]=edg(F[1],F[2],first[F[0]]);
         first[F[0]] = Num++;
         E[Num]=edg(F[0],F[2],first[F[1]]);
         first[F[1]] = Num++;
    }
    int dfs(int occ , int per,int D){
    
        int dd = 0;
        for(int i = first[occ] ; i!=-1 ; i=E[i].next){
            int to = E[i].to ; int dist = E[i].dist ;
            if(to == per) continue ;
            int rt =dfs(to,occ,dist);
            ans = max(dd+rt,ans);
            dd= max(dd,rt);
        }
        return dd+D;
    }
    int main()
    {
          char str[100];
         while(gets(str)){
              memset(first,-1,sizeof(first));
              Num = 0;
               S = 10000;
              if(strlen(str) == 0 ) {
                 printf("0
    ");continue;
              }
              ans = 0 ;
              read(str);
              while(gets(str)){
                  if(strlen(str)==0) break;
                  read(str);
              }
    
             dfs(S,-1,0);
             printf("%d
    ",ans);
         }
         return 0;
    }
    View Code

     uva 10105 这题说的是多项式(x1+x2+x3+x4+x5...+xk)^n   计算x1^n1 * x2^n2 *x3^n3 *...*xk^nk 给了n1 n2..nk 计算该多项式的系数我们可以用二项式展开进行推广(x1 + G(x))^n 这样可以得到一个多项式为 a1*x1^n1*(G(x))^(n-n1),按照二项式展开继续去展开G(x) 可以得到我们要求的 即a1*a2*a3*..*ak;

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn = 15;
    int C[maxn][maxn];
    int indexx[maxn];
    int main()
    {
          for(int i =0 ; i <= 13 ; ++ i){
            C[0][i]=1;
            for(int j = 1 ; j <= i ; ++ j)
              C[j][i] = C[ j - 1 ][ i - 1 ] + C[ j ][ i -1 ];
          }
          int n,k;
          while(scanf("%d%d",&n,&k) == 2 ){
              int ans =1;
              for(int i = 0 ; i< k ; ++ i)
                 scanf("%d",&indexx[i]);
              for(int i = 0 ; i< k ; ++ i){
                ans*=C[indexx[i]][n];
                n-=indexx[i];
              }
             printf("%d
    ",ans);
          }
          return 0;
    }
    View Code

     uva 这题说是 第 I 个 人 获 得  胜 利 的 概 率 抛 一 枚 骰 子 第 一 次 扔 到 3 的人获胜。p*k +R*(P+R(P+R(...)))

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    const double epl = 0.000001;
    int eps(double a){
       if(fabs(a)<epl) return 0;
        return a>0?1:-1;
    }
    int main()
    {
         int N,I,cas;
         double P;
         scanf("%d",&cas);
         while( cas -- ){
             scanf("%d%lf%d",&N,&P,&I);
             double R = 1 ,ans=0,cur=1;
             for(int i = 1 ; i<I ; ++i )
                 R=R*(1-P);
                 cur = R;
             for(int i= I+1 ; i <= N ; ++ i){
                    R=R*(1-P);
                 }
             while(eps((ans+cur*P) - ans )>0){
                  ans+=cur*P;
                  cur=cur*R*(1-P);
             }
             printf("%.4lf
    ",ans);
         }
    
        return 0;
    }
    View Code

     化简后可以发现是一个等比数列 因为(q)《1 当w趋于无穷多项的时候(q)^w趋于0 于是就得到了

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    int main()
    {
         int N,I,cas;
         double P;
         scanf("%d",&cas);
         while( cas -- ){
             scanf("%d%lf%d",&N,&P,&I);
    
             double ans=0;
             if(0.0!=P)
             ans = pow( 1 - P , I - 1 ) * P /( 1 - pow( 1 - P , N ) );
             printf("%.4lf
    ",ans);
         }
    
        return 0;
    }
    View Code

     uva10491 这题说的是 在一个电视节目中 有 N 个 们 其 中 有 a 个 门 后 是 汽 车 b 个 门 后 是 牛 (a+b)等于N  然后 让 你 选 择 你 先 选 中 其 中 一 个 门这个门 就被淘汰了 接下来主持人会将剩下K个门后是牛的 门 打开 在没有打开的门中你再选 选中是汽车的概率 列一个式子(b/N)*(a/(N-K-1))+(a/N)*((a-1)/(N-K-1));

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    
    int main()
    {
           int ncow,ncar,nshow;
           while(scanf("%d%d%d",&ncow,&ncar,&nshow) == 3 ){
                double ans = (double (ncow)/double(ncow+ncar))*(double (ncar)/double(ncow+ncar-1-nshow))
                +  (double (ncar)/double(ncow+ncar))*(double (ncar-1)/double(ncow+ncar-1-nshow));
               printf("%.5lf
    ",ans);
           }
    
    
          return 0;
    }
    View Code

     uva 10759 这题说的是给了 n个正常的骰子然后 计算 这n个骰子的和 大于等于 x 的概率是多大首先用dp计算出dp[n][x] 的值 表示的是 n个骰子的和为x 的方案总数,可以得到这样的动态方程dp[n][x]+=(dp[n-1][x-k])(k>1&&k<=6)这样计算出来的就是这n个点出现和x的方案总数了,因为可以知道最后一个骰子有6中情况 这六种情况对应的上一个骰子在dp[n-][x-k]处的和 然后 垒加一下就ok了 因为对于每个骰子只有6中状态,好了这样就可以和分母进行约分了

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    long long dp[25][150],Th[25];
    int main()
    {
        long long ans =0;
        memset(dp,0,sizeof(dp));
        for(int i =0 ; i<25 ; ++ i ){
            dp[i][i]=1;
            for(int j =i+1; j<= i*6 ; ++ j){
                for(int k = 1 ;k<=6&&j-k+1>=i;  ++ k)
                        dp[i][j]+=dp[i-1][j-k];
    
            }
        }
        Th[0]=1;
        for(int i =1 ; i<=24 ;++i)
            Th[i]=Th[i-1]*6;
        int n,x;
        while(true){
            scanf("%d%d",&n,&x);
            if(n==0&&x==0 ) break;
            long long G=Th[n];
            ans = 0 ;
            for(int i = n ; i < x && i <= n*6 ; ++i )
                ans+=dp[n][i];
            ans=G-ans;
            while(ans%2==0&&ans>0&&G%2==0){
                    G/=2; ans /= 2 ;
            }
            while(ans%3==0&&ans>0&&G%3 == 0){
                   G/=3;  ans/=3;
            }
            if(ans%G==0) printf("%lld
    ",ans/G);
            else printf("%lld/%lld
    ",ans,G);
        }
        return 0;
    }
    View Code

     uva542 这题说的是给了一张表然每个队胜每个队的概率,每次都是两个队进行比赛,淘汰赛,最后计算每个队伍赢得比赛的概率按照他给的计算公式计算

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn = 16;
    double map[maxn][maxn],ans[17][5];
    char str[20][15];
    int num[17],loca[17];
    int main()
    {
        for(int i = 0 ; i< 16 ; ++ i)
            scanf("%s",str[i]);
        for(int i = 0 ; i < 16 ; ++ i)
        for(int j = 0 ; j < 16 ; ++ j){
            scanf("%lf",&map[i][j]);
            map[i][j]/=100.0;
        }
        for(int i = 0 ; i < 16 ;  ++ i ) {num[i]=i; ans[i][0]=1.0;loca[i]=i;}
        for(int i =1 ; i <5 ; ++ i){
            for( int j = 0 ; j < 16 ;  ++ j){
                double t = 0.0;
                int y  = (1<<(i-1));
                int de = (j/y)&1?(j/y)-1:(j/y)+1;
                int st = de * y ;
                for( int k = st ; k/y == de ; ++k ){
                    t += ans[k][i-1]*map[j][k];
                }
                ans[j][i]=t*ans[j][i-1];
            }
        }
        for(int i = 0 ; i< 16 ; ++ i)
            printf("%-10s p=%.2lf%c
    ",str[i],ans[i][4]*100,'%');
        return 0;
    }
    View Code

     uva10277 这题说的是一个人他有一个抽屉的里面有袜子,保证里面最少2只,最多50000 只,给了一个概率就是拿得到两只红袜子的概率  进行 分析后假设总共 C只袜子 t只红袜子 可以得到( t*(t-1)/(c*(c-1)))==P;然后枚举每个可能的值,二分区找可能得到的红袜子的 数量

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const long long  mod =1000;
    typedef long long LL;
    LL p,q;
    LL gcd(LL a, LL b){
       return b==0?a:gcd(b,a%b);
    }
    int main()
    {
        while(true){
            scanf("%lld%lld",&p,&q);
            if(p==0&&q==0) break;
            if((p==0)){
            printf("0 2
    ");
               continue;
            }
            LL mid = 50000;
            LL t= gcd(p,q);
            p/=t;
            q/=t;
            if((q)>(mid*(mid-1))){
                printf("impossible
    ");
                continue;
            }
            LL st=2;
            bool falg = false;
            for( ; st<=50000; ++ st){
                LL L=2 ,R = st;
                LL T = st*(st-1);
                if(T==0 || T%q != 0 ) continue;
                LL t = T/q;
                while(L<=R){
                    mid  = L+(R-L)/2;
                    LL E =mid*(mid-1);
                    if(E%p==0&&(E/p == t)) {
                         falg= true; break;
                    }
                    if(E>(p*t))R=mid-1;
                    else L= mid+1;
                }
                if(falg) break;
            }
           if(falg)
           printf("%lld %lld
    ",mid,st-mid);
            else printf("impossible
    ");
        }
    
         return 0;
    }
    View Code

     uva10169

    题意:两个罐子,A和B;

    开始的时候A中1球、B中2球,但每个罐子中都有一个红球。

    每次从两个罐子分别中取出1个球。记录结果然后放回,放回的同时向两个罐子中分别增加1个白球

    如此反复操作。

    问n次操作中,至少有1次取出的两个球都是红球的概率。 

    若n次操作每次取出的球都是两个红球的概率小数点候有几个连续的0。

    然后计算 操作n次 算拿出大于等于1次 取出的 两个都是红球的概率 , 这个只要1 减去没戏都没有拿到 两个都是红球的的概率就行了 ,第二个是计算全部都是拿出红球的概率在小数点后有多少个0 才到除0外的数 针对这个概率 这样 用log10 去处理一下 可以知道 最后可以化为1/(1*2*2*3*3*4*4*5*5*6*6*7*7*8*9...n*(n+1)) 用log去保存(1*2*2*3*3*4*4*5*5*6*6*7*7*8*9...n*(n+1)) 因为log 10(1) = 0  所以得到了一个 -(E.R)这样一个数 (E)为整数部分 (R) 为小数部分 可以知道 10^(-E) 是离小数点距离的E-1 而后面的 -0.R 不会产生与小数点相邻的为0 的数  因为 最小最小也比0.1大,两个相乘刚好是E的距离于是用log 

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    double ans1[1000000];
    int ans2[1000000];
    int main()
    {
         double t=0 ,ans=1;
         for(int i =1 ;i<1000000; ++ i){
             t+=log10(i)+log10(i+1);
             ans2[i] = int(t);
             double t2=(1.0/i)*(1.0/(i+1));
             ans*=(1-t2);
             ans1[i] = 1.0-ans;
         }
         int n;
         while(scanf("%d",&n)==1){
            printf("%.6lf %d
    ",ans1[n],ans2[n]);
         }
    
        return 0;
    }
    View Code

     uva11181这题说的是一个人有,N个朋友 他们一起去购物 每个人会买东西的概率为 p1,p2,p3,p4,p5...pN,当他们购物完后发现有 r个人买东西了 问每个人买了东西  的概率 (N,r , p1 p2 p3 p4 .. pN 已知)(N<20)

     有贝叶斯公式可以知道 P(Ai/B)=(P(Ai)*P(B/P(Ai)))/((P(Ai)*P(B/P(Ai))i从0到n-1),这里可以知道B为选了r个人的概率 这个以题目例子为例 买买不买+买不买买+不买买买。然后按照这个思路 P(AiB)=P(Ai)*P(B/P(Ai))也就得到了 为第i个买时 总共有r个人买了的概率 ,得到答案 这里主要是在完成了r个人的选择上判断第i个人在这r个人内的概率

    #include <iostream>
    #include <string.h>
    #include <cstdio>
    using namespace std;
    double proba[20];
    double ber[20],mu,ans[20];
    int R[20],n,r;
    void dfs(int ge,int lca,double P){
           if(lca == n && ge ==r ){
                 mu+= P;
                 for(int i = 0 ; i< r ;++ i)
                     ber[R[i]]+=P;
                 return ;
           }
           if(((n-lca)<(r-ge))||(lca>=n)) return ;
           if(ge!=r){
                R[ge]=lca;
              dfs(ge+1,lca+1,P*proba[lca]);
           }
           dfs(ge,lca+1,P*(1-proba[lca]));
    }
    int main()
    {
         int cas=0;
         while(true){
    
            scanf("%d%d",&n,&r);
            if(n==0&&r==0) break;
            mu =0.0;
            for(int i = 0 ; i < n ; ++ i )
                 scanf("%lf",&proba[i]);
            memset(ber,0,sizeof(ber));
            dfs(0,0,1);
            printf("Case %d:
    ",++cas);
            for(int i = 0 ; i<n ;++ i)
                printf("%.6lf
    ",ber[i]/mu);
         }
        return 0;
    }
    View Code

     uva557 这题说的是给了N个孩子(N是偶数)  分给他们汉堡和牛肉饼,用扔硬币的结果分配,正面是 分汉堡 反面是分牛肉饼 , 问最后两个人不需要再扔硬币的概率,刚开始我用正面的方法去解决 好困难 , 后来想想 用1减去要扔的概率 就得到了解 ,  如果要礽那么一定是这样的 最后两个 一定要是一个汉堡和一个牛肉饼, 这样就得到了前面的(n-2) 有(1/2  +   1/2 )^L 二项展开得到 第 L/2+1、项  就是 前面 L个位置放 L/2 汉堡的概率  这样 1- 这个概率 就 可以了 , 用一下 递推  然后 在来个log 保存位数 就ok了

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    double tow[50001*2];
    double C[50001*2];
    int main()
    {
          tow[0]=0;
          for(int i = 1; i<=50001*2 ; ++ i ){
              tow[i]=tow[i-1]-log10(2);
          }
          C[2]=log10(2);
          for(int i = 4 , j = 2 ; i <=50001*2 ; i+=2,++j  )
          {
              C[ i ] = C[ i - 2 ] + log10(i)+log10(i-1) - log10(j) - log10(i-j);
          }
          int n;
          scanf("%d",&n);
          while(n--){
               int k;
               scanf("%d",&k);
               if(k<=2){
                    printf("%.4lf
    ",0.0);
                     continue;
               }
               k-=2;
    
               double A = C[k]+tow[k];
               A = pow(10.0,A);
               A=1-A;
               printf("%.4lf
    ",A);
          }
    
        return 0;
    }
    View Code

     uva10900 一个玩家最初手里有1元钱. 现在, 问他n个问题, 对于每个问题他都有两种选择:1. 退出游戏, 保持当前的钱数;
    2. 回答问题, 如果答错那么钱数清0, 答对则钱数翻倍. 对于每个问题, 玩家答对的概率都是一个t - 1范围内的随机数. 给出n, t, 求出最多可获得钱数的期望.先看看下面这段描述

    Let's have n (n>0) questions left with a starting prize of a. We are going to compute f(n,a).

    Suppose the probability of the correct answer to the first question is p (t<=p<=1). After

    answering the question the prize changes to f(n-1,2*a) with probability p and to 0 with probability 1-p.

    So the expected prize after this question is p*f(n-1,2*a)+(1-p)*0=p*f(n-1,2*a). If this prize is less than

    a the player will choose not to answer and to quit the game keeping the prize of a. Therefore, getting a

    question with probability of the correct answer equal to p the expected prize is max(a,p*f(n-1,2*a)).

      As the probability of the correct answer to the question is uniformly distributed over the range [t,1] the

    average expected prize the player can quit with is 1/(t-1) * int_t^1 max(a,p*f(n-1,2*a)) dp.
    既然他要求的是最高的期望,p*f(k,r); 这句的意思是如果选择了答下一题得到的期望而 f(k,r)就是进入下一题所能得到的期望,那好我们先假设现在达到了第k个问题,那么我们就可以知道在回答 第 k 个 问 题 的 时 候 所 能 得 到 的 期 望 是 多 少 对 于 不 同 的 p 会 得 到 不 同 的  结 果,解 释 一 下   f(n-k,2^k) 表示剩下啊的 n-k 个问题所能取得的期望;那好现在讨论一下这个p(概率)的问题 , 由均匀分布可以得到,在区间上的概率是通过积分求得的,当得到的k个问题的期望的时候 f(n-k,2^k)*p>2^(k-1) 的时候 我们当然希望是继续答题 然而当小于的时候我们就不会继续答题了而是就从住手,这样我们开始讨论 题目中给的 t ,如果t 大于 p 那么就可以得到一定可以继续下去 那么自然在 第k个问题的时候得到的概率就是 (t+1)/2*(fn-k,2^k); 但是当t小于p的时候我们就在想应该将t-1 进行划分 然后进行 一次积分  ∫ max(1<<(k-1) ,  f( n - k, 2 ^k ) * x ) dx|(从t到1) 然后得到了我们想要的

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    int main()
    {
         int n ; double t;
         while(true){
    
            scanf("%d%lf",&n,&t);
    
            if(n == 0 && t == 0.0) break;
            double dp = 1<<n;
            for(int i= n-1 ;i >= 0 ; -- i ){
                double g = (1<<i);
                double a = g/dp;
                if(a<(t)) dp = (1+t)/2 * dp;
                else dp = ( (a - t)*g +dp*(1-a*a)/2 )/(1-t);
            }
            printf("%.3lf
    ",dp);
         }
         return 0;
    }
    View Code

     uva10303 这题说的是给你了 n个点 可以变成几种不同的搜索二叉树,通过枚举左右孩子的个数 我们可以知道 F[n] += F[n-1-i]*F[i](0<=i<=n-1 ) 可以发现是一个递推的过程  是一个卡特兰数 然后得到 了卡特兰数的 通向 Cn+1 =2(2*n+1) /(n+2)  *Cn;  模拟加上大数

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    #include <vector>
    #include <stack>
    using namespace std;
    typedef long long LL;
    const int maxn = 1005;
    const int mod =10000;
    vector<int> P;
    stack<int>S;
    class Bignumber{
        public : LL loc[200];
        public : int len;
        Bignumber(){
          memset(loc,0,sizeof(loc));
          len = 1;
        }
        Bignumber(vector<int> &A){
            int L =A.size();
            for(int i= 0 ; i<L/2 ; ++i){
                int temp =A[i];
                A[i]=A[L-1-i];
                A[L-1-i]=temp;
            }
            int t = A.size()/4 ;
            if(A.size()%4) t++;
            while(L<t*4){ A.push_back(0); L++ ;}
            len = 0;
            for(int i = 0 ; i<t*4 ;  i+=4)
                loc[len++]=A[i+3]*1000+A[i+2]*100+A[i+1]*10+A[i];
            while(len>1&&loc[len-1]==0){ len--; }
        }
       Bignumber operator *(int t){
              Bignumber ans;
              int temp = 0;
              for(int i = 0 ; i< len ;++ i){
                    int r = loc[i]*t+temp;
                    ans.loc[i]=r%mod;
                    temp = r/mod;
                 }
                 ans.len=len;
                 while(temp){
                    ans.loc[ans.len++]=temp%mod;
                     temp/=mod;
                 }
               return ans;
        }
        Bignumber operator /(int t){
    
              LL temp=0;
              for(int i = len-1; i >=0 ; -- i){
                  temp=temp*mod+loc[i];
                  int g = temp/t;
                  temp%=t;
                  P.push_back(g/1000);
                  P.push_back((g%1000)/100);
                  P.push_back((g%100)/10);
                  P.push_back(g%10);
              }
             Bignumber ans=Bignumber(P);
             P.clear();
             return ans;
        }
        void output(){
           printf("%lld",loc[len-1]);
           for(int i = len-2 ; i>=0 ; -- i)
             printf("%04lld",loc[i]);
           printf("
    ");
        }
    };
    Bignumber dp[maxn];
    int main()
    {
          //  freopen("out.txt","w",stdout);
          dp[0].loc[0] =1;
          dp[1].loc[0] = 1;
          for(int i = 1 ; i<1000; ++ i){
               dp[i+1]=dp[i]*((i*2+1)*2);
               dp[i+1]=dp[i+1]/(i+2);
         //     printf("i=%d %d
    ",i,dp[i].len);
          }
          int n;
          while(scanf("%d",&n)==1){
           dp[n].output();
          }
    
         return 0;
    }
    View Code

     uva11176这题说的是给了一个赛季 一个队伍要比赛n场每场赢得概率是 p 求 连胜的期望 LLL(0) LLW(1) LWL(1) WLL(1)WWL(2) WLW(1)WWL(2) WWW(3)

    说说感受吧 这题刚开始 我想用组合的方式解决这个问题但是在偶数个的判重的时候 不知道怎么处理 一卡就卡了 2 天 , 然后发现时 用dp做 dp[i][j] 比i场连赢不超过 j场的概率 ,在考虑第 i场是输还是赢 还是没办法解决那个重复的问题 最后看了解题报告 发现自己脑子略不好使  dp[i][j] 表示第i题 连胜不超过 j题的 概率  可以想象 在求第 i 场是胜利还是失败的时候 可以考虑dp[i][j] =dp[i-1][j] 就是在 dp[i-i][j] 的后面加上 L或者W 如果加L 那肯定是没有重复的如果加的是W 那么得减去一种就是前i-1个的最后是 j个连续的W 这样可以得到前 i-1最后的j个是连续的概率是 dp[i-2-j]*(1-p)*(p)^(j-1) 得到了 第i-1个后面是 连续的j个W的概率 然后 dp[i][j]-=dp[i-2-j][j]*(1-p)*(p)^j 自然当 i-1==j 的时候发现这个是 i - 2 - j =-1 需要特判一下 可以知道这个是 1 - p^n 然后得到了这个点的概率

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn=505;
    double dp[maxn][maxn];
    double tp[maxn];
    int main()
    {
        int n;
        double p;
        while(scanf("%d%lf",&n,&p)==2&&n){
            for(int i = 0 ; i<=n ; ++ i)
                 dp[0][i]=1;
            tp[0]=p;
            for(int i = 1 ; i <=n ;++i  )
                tp[i]=tp[i-1]*p;
            for(int i = 1 ; i<=n  ; ++ i)
            for(int j = 0 ; j<=n ; ++ j){
                dp[i][j]=dp[i-1][j];
                if(i-j-2>=0)
                   dp[i][j]-=dp[i-j-2][j]*(1-p)*tp[j];
                 else if( i-1 == j )
                    dp[i][j]-=tp[j];
            }
            double ans=0;
            for(int i = n-1 ; i>= 0 ; -- i)
                 ans+=(dp[n][i+1]-dp[n][i])*(i+1);
            printf("%.6lf
    ",ans);
        }
        return 0;
    }
    View Code

     hdu 4828 度度熊最近很喜欢玩游戏。这一天他在纸上画了一个2行N列的长方形格子。他想把1到2N这些数依次放进去,但是为了使格子看起来优美,他想找到使每行每列都递增的方案。这题是一个钩子公式 lrjP93黑书 然后我们通过化简 可以得到他与卡特兰数 一样的 递推式这样 我们就剩下处理那个 模 的问题  A+k*mod /b=t%mod  我们已知了 A mod 和 b 然后计算 出t A+mod*k == b*t --- t%=mod  b%=mod;  仍然得到 b*t = A  ;  并不能通过直接除去得到这个解 因为 他们是经过 了 mod 得到的  现在化成 b*x =1  先求出 b关于mod的逆元x 然后 我们根据 模运算 (a*b)%mod  == (a%mod *b%mod)%mod  可以 得到  ((b*x)*A)%mod == A 推出  (b*x*A)%mod ==A  然后就得到了 t = (x*A)%mod; 这样就求得了后一项

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    const long long mod = 1000000007;
    typedef long long LL;
    LL dp[1000005];
    void gcd(LL a,LL b,LL &d,LL &x,LL &y){
         if(!b){
              d=a; x=1; y=0;
         }
         else {
            gcd(b,a%b,d,y,x); y-=x*(a/b);
         }
    }
    LL inv(LL a,LL n){
       LL d,x,y;
       gcd(a,n,d,x,y);
       return d==1?(x+n)%n:-1;
    }
    int main()
    {
             LL p;
          dp[1]=1;
          for(int i =1 ; i <= 1000001 ; ++ i)
             {
                   p=inv(i+2,mod);
                 dp[i+1] = (2*(2*i+1)*dp[i])%mod;
                 dp[i+1] = (p*dp[i+1])%mod;
             }
            int t;
          scanf("%d",&t);
          for(int cas = 1 ; cas <= t ; cas++){
            int n;
            scanf("%d",&n);
            printf("Case #%d:
    ",cas);
            printf("%lld
    ",dp[n]);
          }
    
          return 0;
    }
    View Code

     uva10518 这 题 说 的 是斐 波 那 契 求 值 得 过 程 中 的 调用了几次 函数 F[0] =1 (调用了一次) F[1] =1 调用一次  F[2]调用了 3 次 得到 F[n]=F[n-1]+F[n-2]+1 得到了然后可以知道 [  (1,1),(0,1) ] *[ f[n-2],f[n-2] ]  = [f[n],f[n-1]]  通过这个构造得到了 【[111],[100],[001]】[f[n-1],f[n-2],1] 这样一直下去 得到了我们想要的 然后矩阵快速幂一下

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    typedef long long LL;
    int mod;
    class Matrix{
        public: int M[3][3];
        public: Matrix(){
        }
        public: Matrix(int t){
    
        }
        Matrix operator *(const Matrix &A){
            Matrix ans;
            for(int i = 0 ; i<3 ; ++ i)
                for( int j = 0 ; j<3 ; ++ j){
                  ans.M[i][j] = 0;
                  for( int k = 0 ; k<3 ;++ k)
                   ans.M[i][j]+=(M[i][k]*A.M[k][j]);
                  ans.M[i][j]%=mod;
            }
            return ans;
        }
    };
    
    Matrix pow_t(LL n){
        Matrix A;
         A.M[2][2]=A.M[1][0]=A.M[0][0] =A.M[0][1]=A.M[0][2]=1;
         A.M[1][1]=A.M[1][2]=A.M[2][0] =A.M[2][1]=0;
        Matrix ans;
        ans.M[0][0]=ans.M[1][1]=ans.M[2][2]=1;
        ans.M[0][1]=ans.M[0][2]=ans.M[1][0]=ans.M[1][2]=ans.M[2][0]=ans.M[2][1]=0;
        while(n){
            if(n&1){
                ans = A*ans;
            }
            n>>=1;
            A=A*A;
        }
        return ans;
    }
    int main()
    {
        LL n;
        int cas=0;
        while(true){
                scanf("%lld%d",&n,&mod);
                if( n == 0&& mod == 0 ) break;
            if(n<2){
                printf("Case %d: %lld %d %d
    ",++cas,n,mod,1%mod);
               continue;
            }
            Matrix A = pow_t(n-1);
            printf("Case %d: %lld %d %d
    ",++cas,n,mod,(A.M[0][0]+A.M[0][1]+A.M[0][2])%mod);
        }
        return 0;
    }
    View Code

     uva 10862 这 题 说 的 是 给 了 一 个 点 然 后 有 N 个 人家 每个点只能跟他的左边人家和右边人家 或则从 中 心 节 点 获 得 然 后 问 用 最 少 的 线使得这些点同通信的方案总数

    当最后的那个点不跟别的点联通 是一种 跟 最后一个 一起 最后3 4 5  6  这样可以得到这样的地推式 dp[n] =dp[n-1]*1+dp[n-2]*2+dp[n-3]*3+...+dp[0]*n 然后 化简可以得到

    dp[i]= dp[i-1] +S[i-1] ;S[i] =S[i-1] +dp[i];

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn = 2005;
    const int mod = 100000;
    struct Bignumber{
          int mart[255];
          int len;
          Bignumber(){
             len =1;
             memset(mart,0,sizeof(mart));
          }
          Bignumber operator +(const Bignumber &A){
    
                 int L=max(len,A.len),temp=0;
                 Bignumber ans;
                 for(int i = 0; i<L; ++ i){
                     int r = mart[i]+A.mart[i]+temp;
                     ans.mart[i]=r%mod;
                     temp=r/mod;
                 }
                 ans.len = L;
                 while(temp){
                    ans.mart[ans.len++] =temp%mod;
                    temp/=mod;
                 }
                 return ans;
          }
        Bignumber operator *(int T){
                  Bignumber ans;
                  int temp=0;
                  for( int i =0; i<len ;++ i){
                      int r = mart[i]*T+temp;
                      ans.mart[i]=r%mod;
                      temp=r/mod;
                  }
                  ans.len=len;
                  while(temp>0){
                      ans.mart[ans.len++] = temp%mod;
                      temp/=mod;
                  }
                  return ans;
        }
        void Output(){
             printf("%d",mart[len-1]);
             for(int i =len-2; i>=0; --i)
                printf("%05d",mart[i]);
             printf("
    ");
        }
    };
    Bignumber dp[maxn];
    Bignumber G[maxn];
    int main()
    {
         dp[0].mart[0]=dp[1].mart[0]=1;
         G[1].mart[0]=2;
         for(int i = 2; i<=2000 ; ++i){
             dp[i] = G[i-1]+dp[i-1];
             G[i]=G[i-1]+dp[i];
         }
         int n;
         while(true){
               scanf("%d",&n);
               if(n==0) break;
               dp[n].Output();
         }
        return 0;
    }
    View Code

     uva10334 这题说的是给了一块两个玻璃贴在一块的东西 可以 光在玻璃中穿过的方案总数有多少  我们发现 中间的那个缝只能从边界上来 ,但是旁边的边界可以从 中间或者 旁边了来   于是得到了 dp[i][0] =dp[i-1][0]+dp[i-1][1] dp[i][1]=dp[i-1][0] 不断的去推就得到了解

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn = 2005;
    const int mod = 100000;
    struct Bignumber{
          int mart[255];
          int len;
          Bignumber(){
             len =1;
             memset(mart,0,sizeof(mart));
          }
          Bignumber operator +(const Bignumber &A){
    
                 int L=max(len,A.len),temp=0;
                 Bignumber ans;
                 for(int i = 0; i<L; ++ i){
                     int r = mart[i]+A.mart[i]+temp;
                     ans.mart[i]=r%mod;
                     temp=r/mod;
                 }
                 ans.len = L;
                 while(temp){
                    ans.mart[ans.len++] =temp%mod;
                    temp/=mod;
                 }
                 return ans;
          }
        Bignumber operator *(int T){
                  Bignumber ans;
                  int temp=0;
                  for( int i =0; i<len ;++ i){
                      int r = mart[i]*T+temp;
                      ans.mart[i]=r%mod;
                      temp=r/mod;
                  }
                  ans.len=len;
                  while(temp>0){
                      ans.mart[ans.len++] = temp%mod;
                      temp/=mod;
                  }
                  return ans;
        }
        void Output(){
             printf("%d",mart[len-1]);
             for(int i =len-2; i>=0; --i)
                printf("%05d",mart[i]);
             printf("
    ");
        }
    };
    Bignumber dp[maxn][2];
    Bignumber G[maxn];
    int main()
    {
         dp[0][0].mart[0]=1;
         G[0].mart[0]=1;
         for(int i = 1; i<=1000; ++i)
         {
            dp[i][0]=dp[i-1][0]+dp[i-1][1];
            dp[i][1]=dp[i-1][0];
            G[i]=dp[i][0]+dp[i][1];
         }
         int n;
         while(scanf("%d",&n)==1){
             G[n].Output();
    
         }
        return 0;
    }
    View Code

     uva10236这题求的是 斐波那契数列的中第n哥素数 这个素数的定义是这样的 , 当前出现的fib[i] 不是前面的斐波那契的倍数除了1, 那么这就是斐波那契素数,如果斐波那契数大于10^10 那么只要输出前9位就ok,好现在我们要计算那么不断的用long double 去存数 一但出现大于10^10就 除以10 这样得到解 居然可以这样做,以后遇到处理位数时可以借鉴

    #include <stdio.h>
    #include <algorithm>
    #include <iostream>
    #include <string.h>
    using namespace std;
    const int maxn=1000005;
    const int maxm=10000;
    typedef long long ll;
    bool vis[maxn];
    int prime[maxn];
    int fib[50],fibnum;
    void init(){
       int num=0;
       memset(vis,false,sizeof(vis));
       for(ll i=2; i<1000000; ++i)
       if(vis[i]==false){
          prime[num++]=i;
          for(ll j=i*i; j<1000000; j+=i)
            vis[j]=true;
       }
       fibnum=4;
       fib[1]=fib[2]=1;
       for(int i=3; i<=50; ++i){
           fib[i]=fib[i-1]+fib[i-2];
       }
    }
    long double a[2][2],ans[2][2],c[2][2];
    void solve(long double a[][2],long double b[][2]){
           memset(c,0,sizeof(c));
           for(int i=0; i<2; ++i)
             for(int j=0; j<2; ++j)
                for(int k=0; k<2; ++k)
                  c[i][j]+= a[i][k]*b[k][j];
    }
    void equalop(long double a[][2],long double b[][2]){
          for(int i=0; i<2; ++i)
             for(int j=0; j<2; ++j)
               a[i][j]=b[i][j];
    }
    void pow_bin(int n){
          a[0][1]=a[0][0]=a[1][0]=1;
          a[1][1]=0;
          ans[0][0]=ans[1][1]=1 ; ans[0][1]=ans[1][0]=0;
          n=n-2;
          while(n){
              if( (n&1) != 0 ){
                  solve(ans,a);
                  equalop(ans,c);
              }
              n=n>>1;
              solve(a,a);
              equalop(a,c);
              while(a[0][0]>1000||ans[0][0]>10000){
                 for(int i=0; i<2; ++i)
                     for(int j=0; j<2; ++j)
                       a[i][j]/=10,ans[i][j]/=10;
              }
          }
          long double a = ans[0][0]+ans[0][1];
          while(a<100000000) a*=10;
          int  as=(int)a;
          printf("%d
    ",as);
    }
    int main()
    {
        init();
        int n;
        while(scanf("%d",&n)==1){
              if(n<3){
                 printf("%d
    ",n==1?2:3);
              }else if(n<=14){
                  printf("%d
    ",fib[prime[n-1]]);
              }else{
                  pow_bin(prime[n-1]);
               }
    
        }
        return 0;
    }
    View Code

     uva 10940 这题说的是给了一叠纸牌 从上到下 分别标号为1 到n 然后你每次取最上方的两张牌第一张扔掉第二张放在最底端 直到桌上只剩下一张牌为止问这张牌的最初的编号,500000这么多张 由于数据太大模拟 不现实,那么推推看有什么规律发现,对于大于1的张数,可以先一轮就是除去一半的张数,然后就得到了剩下一半的张数 那么我们之前已经算出来了,这样就直接在返回去找到之前的编号, 对于偶数张 有 dp[i]=dp[i/2]*2 奇数张有dp[i]=(dp[(i/2)+1]-1)*2, 对于偶数张没什么好解释的 对于技术张, 因为最后会留下一张, 他是第二轮的第一张 那留下来的原先编号要往前在退一位

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    const int maxn=500005;
    int dp[maxn];
    int main()
    {
        dp[1]=1;
        for(int i=2; i<=500000; ++i){
              if(i%2==0){
                  dp[i]=dp[i/2]*2;
              }else{
                  dp[i]=(dp[(i/2)+1]-1)*2;
              }
        }
        int n;
        while(scanf("%d",&n)==1&&n!=0){
             printf("%d
    ",dp[n]);
    
        }
        return 0;
    }
    View Code

    uva10519

    平面上有个圆,其中每两个都相交于两点,每三个都无公共点,它们将平面分成块区域,有,求f(n);

    然后 假设有k+1 个圆, 第k+1 个圆与其他k个圆有2k个交点, 那么这个圆被分为2*k 段弧,每段弧对应一块区域那么久多了2*k 块区域 ,那么 得到f(k)=f(k-1)+2*(k-1);

    那么 f[1]=2;f[2]=4,f[3]=8; f[n]=f[n-1]+2*(n-1) ; f[2]-f[1]=2; f[3]-f[2]=4; ....f[n]-f[n-1]=2(n-1)  全部加起来, 就得到了我们要的解ok解决了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    const int maxn=1005;
    struct bignumber{
       int v[maxn];
       int len;
       void clear(){
          memset(v,0,sizeof(v));
          len=1;
       }
       bignumber operator *(bignumber to){
          bignumber ans;
          ans.clear();
          for(int i=0; i<len; ++i)
          for(int j=0; j<to.len; ++j){
              ans.v[i+j]+=v[i]*to.v[j];
              ans.v[i+j+1]+=ans.v[i+j]/10;
              ans.v[i+j]%=10;
          }
           ans.len=max(len+to.len-1,1);
           for(int i=0; i<ans.len; ++i){
               ans.v[i+1]+=ans.v[i]/10;
               ans.v[i]%=10;
           }
           while(ans.v[ans.len]!=0){
             ans.v[ans.len+1]+=ans.v[ans.len]/10;
             ans.v[ans.len]%=10;
             ans.len++;
           }
           while(ans.len>1&&ans.v[ans.len-1]==0) ans.len--;
           return ans;
       }
       bignumber operator -(bignumber to){
           bignumber ans;
           ans.clear();
           ans.len=max(max(len,to.len),1);
           for(int i=0; i<ans.len; ++i)
            ans.v[i]=v[i]-to.v[i];
            for(int i=0; i<ans.len; ++i)
            if(ans.v[i]<0) {
                 ans.v[i]+=10;
                 ans.v[i+1]--;
            }
           while(ans.len>1&&ans.v[ans.len-1]==0) ans.len--;
           return ans;
       }
       bignumber operator +(int a){
           bignumber ans;
           ans.clear();
           ans.len=len;
           ans.v[0]=a;
           for(int i=0; i<ans.len; i++)
            ans.v[i]+=v[i];
           for(int i=0; i<ans.len; ++i){
             ans.v[i+1]+=ans.v[i]/10;
             ans.v[i]%=10;
           }
           ans.len++;
           while(ans.len>1&&ans.v[ans.len-1]==0) ans.len--;
           return ans;
       }
       void print(){
           for(int i=len-1; i>=0; --i)
             printf("%d",v[i]);
       }
    };
    char ss[200];
    bignumber tobignumber(char *str){
        int len=strlen(str);
        reverse(str,str+len);
        bignumber ans;
        ans.clear();
        for(ans.len=len; ans.len>1; ans.len--){
             if(str[ans.len-1]!='0') break;
        }
        for(int i=ans.len-1; i>=0; --i){
             ans.v[i]=str[i]-'0';
        }
        return ans;
    }
    int main()
    {
       while(scanf("%s",ss)==1){
          bignumber n;
          n=tobignumber(ss);
          if(n.len==1&&n.v[0]==0){
             puts("1"); continue;
          }
          bignumber ans;
    
          ans=n*n;
          ans=ans-n;
          ans=ans+2;
          ans.print();puts("");
       }
        return 0;
    }
    View Code

    uva10918 这题说的是要画

    这样的 3*n的图 这是3*12的图 ,然后计算有多少种方法 我们知道长为2 的有3 种 2 ,4 ,6.。。。的分别都有2种,计算最后画这张图有多少种方法

    递推

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    int dp[50];
    int main()
    {
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(int i=2; i<=30; ++i){
              if(i-2>=0) dp[i]+=dp[i-2]*3;
              for(int j=i-4; j>=0; j-=2)
                 dp[i]+=dp[j]*2;
        }
        int n;
        while(scanf("%d",&n)==1&&n>=0){
            printf("%d
    ",dp[n]);
    
        }
    
        return 0;
    }
    View Code

    UVA11069这题挺有意思的可以试试

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    int dp[100][3];
    int main()
    {
        memset(dp,0,sizeof(dp));
        dp[1][0]=1;
        dp[1][1]=0;
        dp[1][2]=0;
        dp[2][0]=1;
        dp[2][1]=1;
        dp[2][2]=0;
        for(int i=3; i<=76; ++i){
             dp[i][0]=dp[i-1][1]+dp[i-1][2];
             dp[i][1]=dp[i-1][0];
             dp[i][2]=dp[i-2][0];
        }
        int n;
        while(scanf("%d",&n)==1){
             printf("%d
    ",dp[n][0]+dp[n][1]);
        }
        return 0;
    }
    View Code

     uva10910 这题说的是给了N,T,P三个数分别是N个同学T个分数每科及格分数为P,且每个同学每科都及格,问最后N个同学有多少分数情况。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    const int maxn=75;
    int dp[maxn][maxn][maxn];
    bool vis[maxn][maxn][maxn];
    void dfs(int n,int t,int p){
          if(vis[n][t][p]) return ;
          if(n==0&&t==0){
             dp[n][t][p]=1;
             vis[n][t][p]=true;
             return ;
          }
          if(n==0&&t!=0){
             dp[n][t][p]=0;
             vis[n][t][p]=true;
             return ;
          }
          vis[n][t][p]=true;
    
          for(int i=p; ; ++i){
             if((t-i)<(n-1)*p) break;
             dfs(n-1,t-i,p);
             dp[n][t][p]+=dp[n-1][t-i][p];
          }
          return ;
    }
    int main()
    {
        memset(vis,false,sizeof(vis));
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=70; i++){
            for(int j=0; j<=70; ++j)
                 for(int k=0; k<=70; ++k)
                  dfs(i,j,k);
        }
        int N,P,T,cas;
        scanf("%d",&cas);
        for(int cc=1; cc<=cas; ++cc){
              scanf("%d%d%d",&N,&T,&P);
            printf("%d
    ",dp[N][T][P]);
        }
        return 0;
    }
    View Code

     uva10328 这题说的是硬币有正反两面H和T然后 你扔硬币 把所有情况记录下来, 问连续k个正面朝上的概率是多大我用 把当前连续 a个 的左边和右边分别放置一个T然后两边的H个数小于等于a 为了避免重复就把左边的个数小于等于a 右边的小于a 然后 用dp去做 dp[i][j] 表示i个骰子 连续j个H的个数, 

    用cnt[i][j] 表示 i次投掷小于等于j个H的方案数,也就是dp[i][j]的 前j项和,枚举连续a个的最右位置,然后用 dp[i][j]+=cnt[前面的个数-1][j]*cnt[后面的个数-1][j-1]{ 因为两边要放T所以都减一 特判 a个H 靠在 左右两端的情况,然后其他情况就用这样的乘法去做} 

    #include <iostream>
    #include <cstdio>
    #include<string.h>
    #include <algorithm>
    using namespace std;
    const int mod =10000;
    struct Bignumber{
        int v[20];
        int len;
        Bignumber(){
            len=1; memset(v,0,sizeof(v));
         }
        Bignumber operator +(Bignumber A){
            Bignumber ans;
            int s=0;
            ans.len=max(len,A.len);
            for(int i=0; i< ans.len; ++i){
                 ans.v[i]=v[i]  + A.v[i] + s ;
                 s=ans.v[i]/mod;
                 ans.v[i]%=mod;
            }
            while(s){
                 ans.v[ans.len]=s%mod;
                 s/=mod;
                 ++ans.len;
            }
            return ans;
        }
        Bignumber operator *(Bignumber A){
             Bignumber ans;
             for(int i=0; i<len; ++i){
                 for(int j=0; j<A.len; j++){
                     ans.v[i+j]+=v[i]*A.v[j];
                     ans.v[i+j+1]+=ans.v[i+j]/mod;
                     ans.v[i+j] %= mod;
                 }
             }
             ans.len=len+A.len-1;
             while(ans.v[ans.len]!=0){
                 ans.v[ans.len+1]+=ans.v[ans.len]/mod;
                 ans.v[ans.len]%=mod;
                 ans.len++;
             }
             while(ans.len>1&&ans.v[ans.len-1]==0) ans.len--;
             return ans;
        }
        void print(){
            printf("%d",v[len-1]);
            for(int i=len-2; i>=0; --i)
                 printf("%04d",v[i]);
        }
    };
    Bignumber dp[101][101];
    Bignumber cnt[101][101];
    int main()
    {
        dp[0][0].v[0]=1;
        cnt[0][0].v[0]=cnt[0][1].v[0]=1;
        dp[1][0].v[0]=1; dp[1][1].v[0]=1;
        cnt[1][0].v[0]=1;cnt[1][1].v[0]=2;
        for(int i=2; i<=100; ++i) cnt[0][i].v[0]=1,cnt[1][i].v[0]=2;
        for( int i=2; i<=100; ++i ){
              cnt[i][0].v[0] = dp[i][0].v[0] = 1;
             for( int j=1; j<=i; ++j ){
                 if( i - j > 0 )
                    dp[i][j] = cnt[i-j-1][j-1] + cnt[i-j-1][j] ;
                 else  dp[i][j].v[0]=1;
    
                 for( int k=j+1; k <i ; k++ )
                     dp[ i ][ j ]=dp[i][j]+ (cnt[ k-j-1 ][ j ]*cnt[i-k-1][j-1]);
                 cnt[i][j]=cnt[i][j-1]+dp[i][j];
             }
             for(int j=i+1; j<=100; j++)
                 cnt[i][j]=cnt[i][j-1];
        }
         int n,k;
         while(scanf("%d%d",&n,&k)==2){
                Bignumber ans;
                for(int i=k; i<=n; ++i)
                    ans=ans+dp[n][i];
                ans.print(); printf("
    ");
         }
        return 0;
    }
    View Code

     uva10157 这题说的是 给了n对括号 , 然后要d 深度的 正确排列, 求出最后可以有多少种排列方式, 我们可以考虑 第一个左括号和与他匹配的右括号,将这个序列分成两部分, 用dp[i][j]表示i对夸号形成 小于等于j的深度的方案总数,我们第一分部分填 小于等于j-1层 外面填小于等于j层 那么 两个部分方案乘起来就是了 dp[i][j]= dp[i][j]+sum(d[i-k][j-1]*dp[i-k-1][j])

    变成了 卡特兰数了 。。。 

    #include <iostream>
    #include <cstdio>
    #include<string.h>
    #include <algorithm>
    using namespace std;
    const int mod =10000;
    struct Bignumber{
        int v[50];
        int len;
        Bignumber(){
            len=1; memset(v,0,sizeof(v));
         }
        void clear(){
           len=1; memset(v,0,sizeof(v));
        }
        Bignumber operator +(Bignumber A){
            Bignumber ans;
            int s=0;
            ans.len=max(len,A.len);
            for(int i=0; i< ans.len; ++i){
                 ans.v[i]=v[i]  + A.v[i] + s ;
                 s=ans.v[i]/mod;
                 ans.v[i]%=mod;
            }
            while(s){
                 ans.v[ans.len]=s%mod;
                 s/=mod;
                 ++ans.len;
            }
            return ans;
        }
        Bignumber operator *(Bignumber A){
             Bignumber ans;
             for(int i=0; i<len; ++i){
                 for(int j=0; j<A.len; j++){
                     ans.v[i+j]+=v[i]*A.v[j];
                     ans.v[i+j+1]+=ans.v[i+j]/mod;
                     ans.v[i+j] %= mod;
                 }
             }
             ans.len=len+A.len-1;
             while(ans.v[ans.len]!=0){
                 ans.v[ans.len+1]+=ans.v[ans.len]/mod;
                 ans.v[ans.len]%=mod;
                 ans.len++;
             }
             while(ans.len>1&&ans.v[ans.len-1]==0) ans.len--;
             return ans;
        }
        Bignumber operator - (Bignumber A){
             Bignumber ans;
             ans.len=max(len,A.len);
              for(int i=0; i<ans.len; ++i)
               ans.v[i]=v[i]-A.v[i];
    
               for(int i=0; i<ans.len; ++i)
                 if(ans.v[i]<0) {
                        ans.v[i]+=mod;
                        ans.v[i+1]--;
                 }
    
            while(ans.len>1&&ans.v[ans.len-1]==0){
                 ans.len--;
            }
    
            return ans;
        }
        void print(){
            printf("%d",v[len-1]);
            for(int i=len-2; i>=0; --i)
                 printf("%04d",v[i]);
        }
    };
    Bignumber dp[300][300];
    int cmp(Bignumber A, Bignumber B){
        if(A.len!=B.len) return A.len>B.len?1:-1;
        for(int i=A.len-1; i>=0; --i)
             if(A.v[i]!=B.v[i] ) return A.v[i]>B.v[i]?1:-1;
         return 0;
    }
    int main()
    {
         dp[0][0].len=1;
         for(int i=1; i<=150; ++i)
             dp[0][i].v[0] = dp[1][i].v[0] = 1;
         for(int i=2; i<=150; i++){
               dp[i][1].v[0]=1;
            for(int j=2; j<=i; ++j){
                for(int k=0; k<i; k++)
                   dp[i][j]=dp[i][j]+( dp[ k ][ j-1 ] * dp[ i-k-1 ][ j ] );
    
            }
                 for(int j=i+1; j<=150; ++j)
                   dp[i][j]=dp[i][j-1];
    
         }
         int n,k;
         while(scanf("%d%d",&n,&k)==2){
    
                    if( (n&1)!=0 || k*2>n){
                          puts("0"); continue;
                    }
                    Bignumber ans;
                 ans= dp[n/2][k]-dp[n/2][k-1];
                ans.print(); printf("
    ");
         }
        return 0;
    }
    View Code

     uva10128 这题说的是给了n个人每个人都有不同的身高, 问从前往后看有p个人可以看到,从后往前看有r个人可以看到,用dp[n][p][r]表示有n个人从前往后看可以看到p个人从后往前看可以看到r个人, 那么n+1个人放哪里 , 假设来的这个人为当前最矮,那么他有三种放法, 第一放在最前面那么可以看到p+1个人, 放在最后面可以看到r+1个人,还有就是放在中间,有n-1 个位置可以放,

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    const int maxn =15;
    long long dp[maxn][maxn][maxn];
    void inti(){
        memset(dp,0,sizeof(dp));
        dp[1][1][1]=1;
        for(int i=2; i<=13; ++i)
            for(int j=1; j<=i;  ++j )
              for(int k =1; k<=i; ++k )
                dp[i][j][k]=dp[i-1][j-1][k]+dp[i-1][j][k-1]+(i-2)*dp[i-1][j][k];
    }
    int main()
    {
        inti();
        int n,p,r;
        int cas;
        scanf("%d",&cas);
        for(int cc=1; cc<=cas; ++cc){
            scanf("%d%d%d",&n,&p,&r);
             printf("%lld
    ",dp[n][p][r]);
    
        }
        return 0;
    }
    View Code

     uva10081

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    using namespace std;
    double dp[105][10];
    int main()
    {
        int k,n;
        while(scanf("%d%d",&k,&n)==2){
            memset(dp,0,sizeof(dp));
            for(int i=0; i<=k; ++i){
                 dp[1][i]=1.0/(k+1);
            }
            for(int i=2; i<=n; ++i){
                 for(int j=0; j<=k; ++j){
                      double a = dp[i-1][j];
                      if(j-1>=0) a+=dp[i-1][j-1];
                      if(j+1<=k) a+=dp[i-1][j+1];
                      dp[i][j]=a/(k+1);
                 }
            }
            double ans=0;
            for(int i=0; i<=k; ++i)
                         ans+=dp[n][i];
                         ans=ans*100;
            printf("%.5lf
    ",ans);
        }
        return 0;
    }
    View Code

    uva10943

    /*
      dp[j][S] 表示 j个数拼成S的方案数
        dp[j][S]+=dp[j-1][S-t](0<=t<=S);
    */
    
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    const int maxn=105;
    const int mod=1000000;
    int dp[maxn][maxn];
    int main()
    {
        dp[0][0]=1;
        for(int i=1; i<=100; ++i)
             for(int j=0; j<=100; ++j)
                for(int k=0; k<=j; ++k)
                     {
                         dp[i][j]+=dp[i-1][j-k];
                          dp[i][j]%=mod;
                     }
         int n,k;
         while(scanf("%d%d",&n,&k)==2){
              if(n==0&&k==0) break;
              printf("%d
    ",dp[k][n]);
    
         }
        return 0;
    }
    View Code

    uva10721

    /*
           考虑第k堆放多少条,
           然后他会从前一堆的相应的条数而来
           dp[n][k][m]+=dp[n-t][k-1][m](t=1 ; t<=n && t<= m);
    */
    
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    const int maxn = 55;
    long long dp[maxn][maxn][maxn];
    int main()
    {
           int n,k,m;
          for(int i=1; i<=50; ++i){
               dp[0][0][i]=1;
               for(int j=1; j<=50; ++j)
                  for(int k=1; k<=50; k++)
                    for(int h=1; h<=i&&h<=k; ++h)
                       dp[k][j][i]+=dp[k-h][j-1][i];
    
          }
           while(scanf("%d%d%d",&n,&k,&m)==3){
    
               printf("%lld
    ",dp[n][k][m]);
           }
    
          return 0;
    }
    View Code

    uva10912

    #include <iostream>
    #include <algorithm>
    #include <string.h>
    #include <cstdio>
    using namespace std;
    long long dp[30][30][400];
    bool vis[30][30][400];
    long long dfs(int st, int L, int S){
             if(vis[st][L][S]) return dp[st][L][S];
             vis[st][L][S]=true;
             int ss =S-st;
             int LL=L-1;
             if( st<27 && LL == 0 && ss == 0 ){
                 return dp[ st ][ L ][ S ] = 1 ;
             }
             if( st > 26 || ( LL==0 && ss!=0 ) || ( LL != 0 && ss == 0 ) || S < st ){
                 return dp[st][L][S]=0;
             }
            for(int i=st+1; i<=26; ++i){
                 if(ss-i<0) break;
                 dp[st][L][S]+=dfs(i,LL,ss);
            }
            return dp[st][L][S];
    }
    int main()
    {
        for(int i=1; i<=26; ++i)
             for(int j=1; j<=26; ++j)
               for(int k=i; k<400; ++k){
                 dfs(i,j,k);
             }
             int L,S,cas=1;
             while(scanf("%d%d",&L,&S)==2){
                    if(L==0&&S==0) break;
                    printf("Case %d: ",cas++);
                  if(L==0&&S==0) break;
                  if(S>=400||L>26){
                     puts("0");
                  }else{
                   long long ans=0;
                    for(int i=1; i<27; ++i)
                         ans+= dp[i][L][S];
                    printf("%lld
    ",ans);
                }
             }
    
        return 0;
    }
    View Code

    uva10616

    /*
         这题说的是给了n个数要求从中选出m个组成能被D整除
         的数。然后我们可以将他们先对D进行取余操作,
         dp[j][m] 表示使用了j个数到达m这个值得方案总数
    
    */
    
    
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    long long dp[205][205];
    int data[205],r[205];
    int main()
    {
        int N,Q,cas=1;
        while(scanf("%d%d",&N,&Q)==2){
                if(N==0&&Q==0) break;
              for(int i=0; i<N; ++i)
                 scanf("%d",&data[i]);
                 printf("SET %d:
    ",cas++);
              for(int i=0; i<Q; ++i){
                  int M,D;
                  scanf("%d%d",&D,&M);
                  for(int j=0; j<N; ++j){
                        r[j]=((data[j]%D)+D)%D;
                    }
                  memset(dp,0,sizeof(dp));
                  dp[0][0]=1;
                  for(int y=0; y<N; ++y )
                     for(int j=M; j>=1; --j)
                       for(int k=200; k>=r[y]; --k)
                          dp[j][k]+=dp[j-1][k-r[y]];
                  long long ans=0;
                  for(int h=0; h<=200; h+=D)
                    ans+=dp[M][h];
                  printf("QUERY %d: %lld
    ",i+1,ans);
              }
    
        }
        return 0;
    }
    View Code

     uva11125

    /*
      状态压缩, 我们通过将 四种颜色压缩放在8进制中, 然后在压4个4进制的值,
      因为每个点和前面不一样,前尾也不一样,那么我们就枚举尾部,然后然后第一个和他也要
      不一样那么我们就把他初始化为 pc pn (分别为前面的颜色和个数) 然后我们到最后的时候判断是否
      已经没有石头了, 判断fc fn pc pn 是否相等,  
    */
    
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    int dp[2000000];
    int A[5],b[5];
    int dfs(int S){
         if(dp[S]!=-1) return dp[S];
         int fc,fn,pc,pn;
         int S1=S;
         pn=S1%4; S1/=4;
         pc=S1%4; S1/=4;
         fn=S1%4; S1/=4;
         fc=S1%4; S1/=4;
         int rr=0;
         for(int i=3; i>=0; --i){
             b[i]=S1%8;
             S1/=8;
             rr+=b[i];
         }
         if(rr==0){
             if(pc==fc&&pn==fn){
                 return dp[S]=1;
             }else return dp[S]=0;
         }
    
         dp[S]=0;
         for(int i=0; i<4; ++i){
             if(pc==i)continue;
             for( int j=1; j<=b[i] && j<=3 ; ++j ){
                 if(pn==j) continue;
                 b[i]-=j;
                 int S0=0;
                 for(int h=0; h<4; ++h) S0=S0*8+b[h];
                 S0= ( ( (S0*4+fc)*4+fn)*4+i )*4+j;
                 dp[S]+=dfs(S0);
                 b[i]+=j;
             }
         }
         return dp[S];
    }
    void sovle(){
        int n;
        scanf("%d",&n);
        int S=0;
        memset(A,0,sizeof(A));
        for(int i=0; i<n; ++i){
             scanf("%d",&A[i]);
             S+=A[i];
        }
        if(S==0){
             printf("1
    "); return ;
        }
        S=0;
        for(int i=0; i<4; ++i){
             S=S*8+A[i];
        }
        int ans=0;
        for(int i=0; i<4; ++i){
             for(int j=1; j<=A[i]&&j<=3; ++j){
                  int t = ( ( ( S*4 + i ) * 4 + j ) * 4 + i )*4 + j;
                  ans+=dfs(t);
             }
        }
        printf("%d
    ",ans);
    }
    int main()
    {
       int cas;
       scanf("%d",&cas);
        memset(dp,-1,sizeof(dp));
        for(int cc=1; cc<=cas; ++cc){
            sovle();
        }
    
        return 0;
    }
    View Code

    uva10205

    #include <iostream>
    #include <cstdio>
    #include<algorithm>
    #include <string.h>
    using namespace std;
    struct card{
      char s1[10];
      char s2[15];
      int loc;
      bool operator < (const  card A)const{
         return loc<A.loc;
      }
    }T[55],P[55];
    char s1[15][10]={ "2" ,"3","4","5","6","7","8","9","10","Jack","Queen","King","Ace" };
    char s2[5][15]={  "of Clubs","of Diamonds","of Hearts","of Spades" };
    int turn[105][60];
    void solve(int d){
       for(int i=1; i<=52; ++i){
            int  loc  = turn[d][i];
            P[loc].loc=i;
       }
       sort(P+1,P+53);
    }
    int main()
    {
        int num=1;
        for(int i=0; i<4; ++i){
             for(int j=0; j<13; ++j){
                 strcpy(T[num].s1,s1[j]);
                 strcpy(T[num].s2,s2[i]);
                 T[num].loc=num;
                 num++;
             }
        }
        int cas;
        scanf("%d",&cas);
        for(int cc=0; cc<cas; ++cc){
                if(cc) printf("
    ");
            for(int i=1; i<=52; ++i)
                P[i]=T[i];
            int n;
            scanf("%d",&n);
                for(int i=1; i<=n; ++i)
                  for(int j=1; j<=52; ++j)
                      scanf("%d",&turn[i][j]);
            for(int i=0; i<n; ++i){
                   int d;
                   scanf("%d",&d);
                   solve(d);
            }
            for(int i=1; i<=52; ++i)
                 printf("%s %s
    ",P[i].s1,P[i].s2);
        }
        return 0;
    }
    View Code

    uva701

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <string.h>
    #include <cmath>
    using namespace std;
    long long v;
    double Log(double a , double b){
       return log10(b)/log10(a);
    }
    int main()
    {
        while(scanf("%I64d",&v)==1){
             int len=1;
             long long d=v;
             d/=10;
             while(d){
                  d/=10; len++;
             }
             bool falg=false;
             for(int i = len+1 ; ; ++i ){
                  double a,b;
                 a=Log(2,v)+i*Log(2,10);
                 b=Log(2,v+1)+i*Log(2,10);
                 for(long long k = a; k<b; ++k){
                      if(k>=a&&k<=b){
                          cout<<k<<endl;
                         falg=true;break;
                      }
                 }
                 if(falg) break;
             }
        }
        return 0;
    }
    View Code

    uva696

    #include <stdio.h>
    
    int n, m, ans;
    
    int main() {
        while (~scanf("%d%d", &n, &m) && n + m) {
        if (n == 1) ans = m;
        else if (m == 1) ans = n;
        else if (n == 2) ans = m / 4 * 4 + ((m % 4 * 2) >= 4 ? 4 : (m % 4 * 2));
        else if (m == 2) ans = n / 4 * 4 + ((n % 4 * 2) >= 4 ? 4 : (n % 4 * 2));
        else ans = (m * n + 1) / 2;
        printf("%d knights may be placed on a %d row %d column board.
    ", ans, n, m);
        }
        return 0;
    }
    View Code

    uva254

    /*
         这题说的是给了 n个 盘子的汉诺塔, 第
         m步, 3个棍子上的个数分别是多少,
         我们每次确定最大的那个会是在那个棍子上,
         我们知道汉诺塔的 递归过程, 最简的步数是2^n -1下 我们利用这两点,贪心的去做
    */
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    const int mod=10;
    struct Bignumber{
        int v[100];
        int len;
        Bignumber(){
            memset(v,0,sizeof(v));
            len=1;
         }
        void clear(){
            memset(v,0,sizeof(v));
            len=1;
        }
        Bignumber operator *(int n){
            Bignumber ans;
            ans.len=len;
            int res=0;
            for(int i=0; i<ans.len; ++i){
                ans.v[i]=res+v[i]*n;
                res=ans.v[i]/mod;
                ans.v[i]%=mod;
            }
            while(res>0){
                 ans.v[ans.len]=res%mod;
                 ans.len++; res/=mod;
            }
            return ans;
        }
        Bignumber operator -( Bignumber A){
            Bignumber ans;
            ans.len=max(A.len,len);
            for(int i=0; i<ans.len; ++i)
                 ans.v[i]=v[i]-A.v[i];
            for(int i=0; i<ans.len; ++i)
                 if(ans.v[i]<0) ans.v[i]+=mod, ans.v[i+1]--;
            while(ans.len>1&&ans.v[ans.len-1]==0){
                 ans.len--;
            }
            return ans;
        }
        Bignumber operator -( int a){
            Bignumber ans;
            ans.len=len;
            ans.v[0]=v[0]-a;
            for( int i = 1 ; i < ans.len ; ++ i )
                 ans.v[i]=v[i];
            for(int i=0; i<ans.len; ++i)
                 if(ans.v[i]<0) ans.v[i]+=mod, ans.v[i+1]--;
            while(ans.len>1&&ans.v[ans.len-1]==0){
                 ans.len--;
            }
            return ans;
        }
        void print(){
            for(int i=len-1; i>=0; --i)
                 printf("%d",v[i]);
        }
        bool operator < (Bignumber A){
            if( A.len!=len) return len<A.len;
            for(int i=len-1; i>=0 ;--i )
                 if(v[i]!=A.v[i] ) return v[i]<A.v[i];
            return false;
        }
        bool operator <= (Bignumber A){
            if( A.len!=len) return len<A.len;
            for(int i=len-1; i>=0 ;--i )
                 if(v[i]!=A.v[i] ) return v[i]<A.v[i];
            return true;
        }
    }T[105];
    int num[5];
    char str[405];
    Bignumber solve(){
        int len=strlen(str);
        Bignumber ans;
        ans.len=len;
        for(int i=len-1; i>=0; --i){
              ans.v[len-1-i]=str[i]-'0';
        }
        while(ans.len>1&&ans.v[ans.len-1]==0)
             ans.len--;
        return ans;
    }
    void dfs(int A,int B, int C, int leaf, Bignumber lest){
         if(lest.len==1&&lest.v[0]==0) {
                num[A]+=leaf;
                return;
         }
         for(int i=1; i<=leaf; ++i){
            if( lest <= T[i] && T[i-1] < lest ){
                    num[A]+= leaf-i;
                    Bignumber ans=( lest - T[i-1] ) -1;
                 if(i&1){
                    num[B]++;
                    dfs(C,A,B,i-1,ans);
                 }else{
                    num[C]++;
                    dfs(B,C,A,i-1,ans);
                 }
                break;
            }
         }
    }
    int main()
    {
       Bignumber a;
       a.v[0]=1;
        for(int i=1; i<101; ++i){
             a=a*2;
    
             T[i]=a-1;
        }
        int n;
        while(scanf("%d%s",&n,str)==2){
             Bignumber ans=solve();
             if(n==0&&ans.len==1&&ans.v[0]==0) break;
             memset(num,0,sizeof(num));
             dfs(0,1,2,n,ans);
             printf("%d %d %d
    ",num[0],num[1],num[2]);
        }
        return 0;
    }
    View Code

    uva10994

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int solve(int v){
    
       while(true){
           if(v==0) return 0;
           if(v%10) return v%10;
           v/=10;
       }
    }
    int W[100];
    long long H[]={0,1,3,6,10,15,21,28,36,45};
    long long cnt(long long  R){
         int len=1;
         W[0]=R%10;R/=10;
         while(R){
             W[len++]=R%10;R/=10;
         }
         for(int i=0; i<len/2; ++i){
             int t=W[i];
             W[i]=W[len-1-i];
             W[len-1-i]=t;
         }
         long long ans=0;
         long long num=0;
         for(int i=0; i<len; ++i){
            ans+= num * (45LL) + H[ W[i] ];
            num=num*10+W[i];
         }
         return ans;
    }
    int main()
    {
        long long p,q;
        while(scanf("%lld%lld",&p,&q)==2){
                if(p<0||q<0) break;
           long long  ans1=0,ans2=0;
           long long R=solve(p);
           ans1=cnt(p);
           ans2=cnt(q);
           printf("%lld
    ",ans2-ans1+R);
        }
        return 0;
    }
    View Code

    uva10570

    /*
       枚举每一个1可能的位置
    */
    
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    const int maxn=5050;
    int F[maxn];
    int t[maxn];
    int tr[maxn];
    int solve(int r[], int n){
        for(int i=0; i<n; ++i){
                t[i]=r[i];
                tr[t[i]]=i;
        }
         int v=0;
         int ans=0;
        for(int i=0; i<n; ++i){
            v=(v+1)%n;
             if(t[i]!=v){
                  ans++;
                  int trr=tr[v];
                  t[ trr ] = t[ i ];
                  tr[ t[i] ] = trr;
                  t[i]=v;
                  tr[v]=i;
             }
        }
        return ans;
    }
    int main()
    {
        int n;
        while(true){
             scanf("%d",&n);
             if(n<=2) break;
             int ans=100000;
             for(int i=0; i<n; ++i){
                 scanf("%d",&F[i]);
                 F[i]--;
                 F[i+n]=F[i];
             }
    
             for(int i=0; i<n; ++i){
                   ans=min( ans , solve(F+i,n) ) ;
             }
             for(int i=0; i<n/2; ++i){
                 int t = F[i];
                 F[i]=F[n-i-1];
                 F[n-i-1]=t;
                 t=F[i+n];
                 F[i+n]=F[n+n-i-1];
                 F[n+n-i-1]=t;
             }
             for(int i=0; i<n; ++i){
                   ans=min( ans , solve(F+i,n) ) ;
             }
             printf("%d
    ",ans);
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    纪中集训 Day 2
    纪中集训 Day1
    纪中集训 Day 0?
    BZOJ 1033: [ZJOI2008]杀蚂蚁antbuster(模拟)
    BZOJ 3527: [Zjoi2014]力(FFT)
    wikioi 3132 高精度乘法(FFT)
    BZOJ 1085: [SCOI2005]骑士精神(A*算法)
    BZOJ 1009 :[HNOI2008]GT考试(KPM算法+dp+矩阵快速幂)
    BZOJ 1019 :[SHOI2008]汉诺塔(递推)
    BZOJ 1021 :[SHOI2008]Debt 循环的债务 (DP)
  • 原文地址:https://www.cnblogs.com/Opaser/p/3766718.html
Copyright © 2011-2022 走看看