BZOJ 1044 [HAOI2008]木棍分割

第一问就是一个二分不说了。。

第二问可以设F[i][j]表示切i次,前j个木棍切完的方案数

转移:F[i][j] = sum(F[i - 1][k])  (Prefix[i] - Prefix[k] <= Ans)  (Prefix是前缀和) (Ans是第一问的答案)

显然暴力转移是O(mn^2)的显然过不了,必须要优化

 

网上许多做法是用决策单调性来做,对上一个阶段维护一个滑动窗口。但我觉得这样做尽管容易理解,但是需要考虑诸多细节,不一定会写对。

我们会发现每次转移中,每个j对应的最小的k是一定的。所以我们可以先O(nlogn)预处理出每个j所对应的最小的k(也就是满足从k+1~j的和不大于Ans最小的k)

然后维护一个状态的前缀和,就可以在dp转移的时候做到O(1)。这样的复杂度也是O(nm)的。

 

  1 #include <cstdio>
  2 #include <algorithm>
  3 #define NOW (j & 1)
  4 #define LAST ((j & 1) ^ 1)
  5 
  6 using namespace std;
  7 
  8 const size_t Max_N(50050);
  9 const size_t Max_M(1050);
 10 const int MOD(10007);
 11 
 12 int N, M;
 13 int L[Max_N];
 14 int Prefix[Max_N];
 15 
 16 int Farest[Max_N];
 17 int F[2][Max_N];//F[i][j]表示切i次,前j个木棍切完的方案数
 18 int F_Prefix[2][Max_N];
 19 //F[i][j] = sum(F[i - 1][k])  (Prefix[i] - Prefix[k] <= Ans)
 20 //-Prefix[k] <= Ans - Prefix[i]
 21 //Prefix[k] >= Prefix[i] - Ans
 22 
 23 int l, r, mid;
 24 
 25 int Ans, Ans2;
 26 
 27 void init()
 28 {
 29     l = r = 0;
 30     scanf("%d%d", &N, &M);
 31     for (int i = 1;i <= N;++i)
 32     {
 33         scanf("%d", L + i);
 34         Prefix[i] = Prefix[i - 1] + L[i];
 35         l = max(l, L[i]), r += L[i];
 36     }
 37     ++r;
 38 }
 39 
 40 bool check(const int &d)
 41 {
 42     int need(0), now(L[1]);
 43     for (int i = 2;i <= N;++i)
 44         if (now + L[i] <= d)
 45             now += L[i];
 46         else
 47         {
 48             now = L[i];
 49             ++need;
 50         }
 51     return need <= M;
 52 }
 53 
 54 void Ask_1()
 55 {
 56     while (l < r)
 57     {
 58         mid = l + ((r - l) >> 1);
 59         if (check(mid))
 60             r = mid;
 61         else
 62             l = mid + 1;
 63     }
 64     Ans = l;
 65 }
 66 
 67 int lookup(const int &Key)
 68 {
 69     int l(0), r(N + 1), mid;
 70     while (l < r)
 71     {
 72         mid = l + ((r - l) >> 1);
 73         if (Prefix[mid] >= Key)
 74             r = mid;
 75         else
 76             l = mid + 1;
 77     }
 78     return l;
 79 }
 80 
 81 void Ask_2()
 82 {
 83     for (int i = 1;i <= N;++i)
 84         Farest[i] = lookup(Prefix[i] - Ans);
 85     for (int i = 1;i <= N;++i)
 86         if (Prefix[i] <= Ans)
 87         {
 88             F[0][i] = 1;
 89             F_Prefix[0][i] = i % MOD;
 90         }
 91         else
 92             F_Prefix[0][i] = F_Prefix[0][i - 1];
 93     Ans2 = F[0][N];
 94     for (int j = 1;j <= M;++j)
 95     {
 96         for (int i = 0;i - 1 < j;++i)
 97             F_Prefix[NOW][i] = F[NOW][i] = 0;
 98         for (int i = j + 1;i <= N;++i)
 99         {
100             if (Farest[i])
101                 F[NOW][i] = (F_Prefix[LAST][i - 1] - F_Prefix[LAST][Farest[i] - 1] + MOD) % MOD;
102             else
103                 F[NOW][i] = F_Prefix[LAST][i - 1];
104             F_Prefix[NOW][i] = (F_Prefix[NOW][i - 1] + F[NOW][i]) % MOD;
105         }
106         (Ans2 += F[NOW][N]) %= MOD;
107     }
108 }
109 
110 inline
111 void Get_Ans()
112 {
113     printf("%d %d", Ans, Ans2 % MOD);
114 }
115 
116 int main()
117 {
118     init();
119     Ask_1();
120     Ask_2();
121     Get_Ans();
122     return 0;
123 }
BZOJ 1044

 

你可能感兴趣的:(BZOJ 1044 [HAOI2008]木棍分割)