【dp】6146 - Ultimate Device 2012合艾赛区F题

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4157

      非常遗憾的一道题,比赛时候很早想出来,但是由于某些原因最后拖到绝杀时刻,且被催的绝杀TLE,没有通过。

      题意:给出n(<=100)个数字,大小在1~500内,每次取任意数字,一共有2^n种方案,对每种方案中被取出数字的最小公倍数求和。结果MOD 10007.

      这个问题可以得到一个非常暴力的dp, 对于一个最小公倍数显然可以表示成这种形式:p1^i1* p2^i2 * ...pk^ik,其中p是小于500的质数。我们可以把i1~ik压缩一下然后做成每个数取于不取的背包。但是,500以内的质数非常多,针对这个问题我当时想出一个优化,如果不考虑p^2>500这种质数,则只剩下2,3,5,7,11,13,17,19,其中每个质数出现次数最多是8,5,3,3,2,2,2,压缩一下此时的状态数就只有9*6*4*4*3*3*3*3=69984,正好在题目要求复杂度内。

      接下来讨论如何忽略p^2>500这种质数,直接讲我的做法,我们dp过程种dp[i][sta],表示的是前i个数任意取后,最小公倍数表示成sta的方案是多少种,如果后面已经不再含有这种质数(A),且我们可以知道哪些方案至少包含一个含有这种质数p(B),则只需要将这些方案数*p即可,由于条件A,所以我们不再关心哪些方案是已经含有质数p了,但是已经含有质数p的方案权重是没有含有p的p倍,所以需要条件B。到这里做法就出来了,就是把含有这种质数的数字放到连续的一段内,假设这一段是i+1~j...则dp[j][sta]-dp[i][sta]便是至少包含一个质数p的方案,所以此时额外转移如下:dp[j][sta] = (dp[j][sta]-dp[i][sta])*p + dp[i][sta]

     最后附程序:

View Code
  1 //By Lin
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #define sqr(x) ((x)*(x))
  7 #define MOD 10007
  8 using namespace std;
  9 
 10 int        prime[100],cnt;
 11 int        num[8],n,data[105];
 12 int        chuan[70000][501],tol;
 13 int        val[70000],dp[2][70000],tmp[70000];
 14 vector<int> g[100];
 15 
 16 void    init(){
 17     cnt = 0;
 18     for (int i = 2; i<=500; i++){
 19         bool flag = true;
 20         for (int j = 0; j<cnt && sqr(prime[j])<=i; j++)
 21             if ( i%prime[j] == 0 ) { flag = false; break; }
 22         if ( flag ) prime[cnt++] = i;
 23     }
 24     tol = 1; 
 25     for (int i = 0; i<8; i++){
 26         num[i] = 0; int k = 1; 
 27         while ( k<500 ) k *= prime[i], num[i]++;
 28         tol *= num[i];
 29     }
 30     int    g[501][8];
 31     for (int i = 1; i<=500; i++)
 32         for (int j = 0; j<8; j++){
 33             g[i][j] = 0; int k = 1; 
 34             while ( i%(k*prime[j])== 0 ) g[i][j]++,k*=prime[j];
 35         }
 36     for (int i = 0; i<tol; i++ ) {
 37         int now[8];
 38         for (int j = 0 , x = i; j<8; j++) now[j] = x%num[j] , x/= num[j];
 39         val[i] = 1; 
 40         for (int j = 0; j<8; j++) 
 41             for (int k = 0; k<now[j]; k++) val[i] = val[i]*prime[j]%MOD;
 42         for (int j = 1; j<=500; j++){
 43             chuan[i][j] = max( now[7] , g[j][7] );
 44             for (int k = 6; k>=0; k--){
 45                 chuan[i][j] *= num[k];
 46                 chuan[i][j] += max( now[k] , g[j][k] );
 47             }
 48         }
 49     }
 50 }
 51 
 52 int        main(){
 53     init(); 
 54     int cas ,tt = 0; 
 55     scanf("%d", &cas );
 56     while ( cas -- ) {
 57         scanf("%d", &n );
 58         for (int i = 0; i<cnt; i++) g[i].clear();
 59         for (int i = 0; i<n; i++) {
 60             int x;
 61             scanf("%d", &x );
 62             bool flag = true;
 63             for (int j = 8; j<cnt; j++) 
 64                 if ( x%prime[j] == 0 ) {
 65                     flag = false;
 66                     g[j].push_back(x);
 67                     break;
 68                 }
 69             if ( flag ) g[0].push_back(x);
 70         }
 71         int p = 0 , q = 1; 
 72         memset( dp , 0 , sizeof(dp) );
 73         dp[0][0] = 1; 
 74         for (int i = 0; i<cnt; i++) {
 75             if ( g[i].size() == 0 ) continue;
 76             if ( i ) memcpy( tmp , dp[p] , sizeof(dp[p]) );
 77             for (int j = 0; j<g[i].size(); j++ ) {
 78                 int x = g[i][j];
 79                 memcpy( dp[q] , dp[p] , sizeof(dp[p]) );
 80                 for (int k = 0; k<tol; k++) {
 81                     if ( dp[p][k] == 0 ) continue;
 82                     dp[q][ chuan[k][x] ] += dp[p][k];
 83                     dp[q][ chuan[k][x] ] %= MOD;
 84                 }
 85                 swap(p,q);
 86             }
 87             if ( i ) {
 88                 for (int k = 0; k<tol; k++) 
 89                     if ( dp[p][k] || tmp[k] ) dp[p][k] = ((dp[p][k]-tmp[k]+MOD)*prime[i]+tmp[k])%MOD;
 90             }
 91         }
 92         int ans = 0;
 93         dp[p][0] += MOD-1;
 94         dp[p][0] %= MOD;
 95         for (int k = 0; k<tol; k++){
 96             ans += dp[p][k]*val[k];
 97             ans %= MOD;
 98         }
 99         printf("Case %d: %d\n" , ++tt ,  ans );
100         
101     }
102     return 0;
103 }
posted @ 2012-12-03 20:15  lzqxh  阅读(323)  评论(0编辑  收藏  举报