代码改变世界

srm555 div1

2012-09-23 02:11  macaroniz  阅读(240)  评论(0)    收藏  举报

255pt:

构造出5^n的所有二进制串,然后dp即可。dp[i][j]表示考虑0~i构成的子串,分成j份是否可行,转移方程为dp[i][j] = dp[i][j] || (dp[i'][j-1] && (i'+1~i构成的子串是合法的))

 1 vector<string> arrPow;
 2 bool dp[55][55];
 3 class CuttingBitString
 4 {
 5 public:
 6 string s;
 7 void getArrPow()
 8 {
 9     arrPow.clear();
10     unsigned long long k = 1;
11     for(int i = 0;i <= 25;i++)
12     {
13         string strA = "";
14         unsigned long long tmp = k;
15         while(tmp != 0)
16         {
17             if(tmp % 2 == 0) strA += "0";
18             else strA += "1";
19             tmp = tmp / 2;
20         }
21         string strB = "";
22         bool flag = false;
23         for(int j = strA.length()-1;j >= 0;j--)
24         {
25             if(strA[j] != '0')
26                 flag = true;
27             if(flag)
28                 strB += strA[j];
29         }
30         arrPow.push_back(strB);
31        k*=5;
32     }
33 }
34 
35 bool OK(int x,int y)
36 {
37     string str = "";
38     for(int i = x;i <= y;i++)
39         str += s[i];
40     for(int i = 0;i < arrPow.size();i++)
41         if(str == arrPow[i])
42             return true;
43     return false;
44 }
45 int getmin(string _S)
46 {
47     s = _S;
48     getArrPow();
49     memset(dp,false,sizeof dp);
50     for(int i = 0;i < s.length();i++)
51     {
52         if(OK(0,i))
53         {
54             dp[i][1] = true;
55         }
56     }
57     for(int i = 1;i < s.length();i++)
58         for(int j = 2;j <= s.length();j++)
59             for(int ii = 0;ii < i;ii++)
60                 if(OK(ii+1,i) && dp[ii][j-1])
61                     dp[i][j] = true;
62     for(int i = 0;i <= s.length();i++)
63     {
64         if(dp[s.length()-1][i] == 1)
65         {
66             return i;
67         }
68     }
69     return -1;
70 }
71 }

 

555pt:

有个重要的观察:

经过一系列操作后,1的个数只与每一行和每一列操作次数的奇偶性有关,和具体次数无关。

因此我们可以推出公式,假设有且只有x行被操作了奇数次,有且只有y列被操作了奇数次,h为行数,w为列数,s为最后1的个数,那么有等式:x * (w-y) + (h-x) * y = s。

因此我们只需要枚举x,y就可以知道究竟哪些情况是合法的,假设我们知道了(x1,y1)是一组合法的情况,那么该情况下的方案数可以直接推得:

方案数 =  calc(h,Rcount,x1) * calc(w,Ccount,y1)

其中 calc(n,m,x) 的抽象含义为将m个相同的球放在n个不同的盒子中,并且有且只有x个盒子中包含奇数个球。

计算方法为: calc(n,m,x) = C(x,n) * C(n-1,n+(m-x)/2 - 1), C(a,b)表示组合数

 

 1 const int mo = 555555555;
 2 
 3 int C[3000][3000];
 4 class XorBoard
 5 {
 6 public:
 7 void init()
 8 {
 9     memset(C,0,sizeof C);
10     C[0][0] = 1;
11     for(int i = 1;i < 3000;i++)
12     {
13         C[i][0] = 1;
14         for(int j = 1;j <= i;j++)
15             C[i][j] = (C[i-1][j-1] + C[i-1][j]) % mo;
16     }
17 }
18 
19 long long calc(int n,int m,int x)
20 {
21     if((m - x) % 2 != 0) return 0;
22     if(m < x) return 0;
23     int times = (m-x)/2;
24     long long ret = 1;
25     ret = (1ll * C[n+times-1][n-1] * C[n][x]) % mo;
26     return ret;
27 }
28 
29 int minx(int a,int b)
30 {
31     return a < b ? a : b;
32 }
33 
34 int count(int H, int W, int Rcount, int Ccount, int S)
35 {
36     init();
37     int ans = 0;
38     int n = minx(H,Rcount),m = minx(W,Ccount);
39     int ii = ( Rcount % 2 == 0 )? 0 : 1;
40     int jj = ( Ccount % 2 == 0 )? 0 : 1;
41     for(int i = ii;i <= n;i++)
42         for(int j = jj ;j <= m;j++)
43             if((i * (W - j) + (H - i) * j) == S)
44             {
45                 long long temp = (calc(H,Rcount,i) * calc(W,Ccount,j)) % mo;
46                 ans = (ans + temp) % mo;
47             }
48     return ans;
49 }
50 }