NOIP模拟42

T1:

  树形DP最大权独立集,发现取模意义下无法取max进行转移
    又发现转移只需要取max,于是采用log对数据进行放缩,
    判断最优决策进行转移
    挂分:最后一步忘记log放缩,直接对DP数组取max而非对
    放缩数组取max再判断答案

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I long long
 4 #define C char
 5 #define V void
 6 #define D double
 7 const I N = 2e5 + 3;
 8 const I mod = 1e9 + 7;
 9 I n,tot,to[N << 1],nxt[N << 1],head[N],w[N],dp[N][2];
10 D f[N][2];
11 inline I read () {
12     I x(0),y(1); C z(getchar());
13     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
14     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
15     return x * y;
16 }
17 inline V found (I x,I y) {
18     to[++tot] = y, nxt[tot] = head[x], head[x] = tot;
19     to[++tot] = x, nxt[tot] = head[y], head[y] = tot;
20 }
21 V dfs (I x,I edge) {
22     dp[x][0] = 1, dp[x][1] = w[x] % mod, f[x][1] = log2(w[x]);
23     for (I i(head[x]),y(to[i]); i ;i = nxt[i],y = to[i]) if ((i ^ edge) != 1) { dfs (y,i);
24       f[x][1] += f[y][0]; dp[x][1] = (dp[x][1] * dp[y][0]) % mod;
25       f[x][0] += f[y][0] > f[y][1] ? (dp[x][0] = (dp[x][0] * dp[y][0]) % mod,f[y][0]) : (dp[x][0] = (dp[x][0] * dp[y][1]) % mod,f[y][1]);
26     }
27 }
28 signed main () {   
29     n = read(); tot = 1;
30     for (I i(1);i <= n; ++ i) w[i] = read();
31     for (I i(1);i <  n; ++ i) found (read(),read());
32     dfs (1,0); I tmp = (f[1][0] > f[1][1] ? dp[1][0] : dp[1][1]) % mod;
33     printf ("%lld\n",tmp);
34 }
View Code

T2:

  考虑性质,在题目要求下,2的次幂被完全划分,即i相同基数

与2的次幂积被有序划分为两个集合。

  发现进存在奇偶两种情况,宏观考虑,所有偶性元素等价,仅

奇性元素会对答案造成贡献,即最劣情况下为全选偶链,最优情况

为能选奇链选奇链,于是答案位于一个区间[L,R],可以发现达到m的

方案数为C(奇链数,所需奇链数)。

  于是问题转化为求解奇链个数,考虑链数的计算本质,即对于

奇数2k+1,有(2k+1)*2^p <= n < (2k+1)*2^(p+1),于是解不等式即可

  考场未模拟过程,对问题性质分析不完全,当问题进行到奇偶链

分析时,并未发现奇偶链贡献的关键不同,而是直接通过解不等式计

算链长,并对链长生成函数求解,事实上并不必要,时间复杂度也无

法通过。注意分析问题性质。

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I long long
 4 #define C char
 5 #define V void
 6 const I mod = 1e7 + 19;
 7 I q,logn,n,num,tot,tmp,J[mod],Y[mod];
 8 inline I read () {
 9     I x(0),y(1); C z(getchar());
10     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
11     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
12     return x * y;
13 }
14 inline I qpow (I a,I b) { I res(1);
15     for (; b ;a = a * a % mod, b >>= 1)
16       if (b & 1) res = res * a % mod;
17     return res;
18 }
19 inline I getc (I n,I m)  { 
20     if (m < 0 || m > n) return 0;
21     return J[n] * Y[m] % mod * Y[n - m] % mod; 
22 }
23 inline I Lucas (I n,I m) { 
24     if (m < 0 || m > n) return 0; if (!m || n == m) return 1; 
25     return Lucas (n / mod,m / mod) * getc (n % mod,m % mod) % mod;
26 }
27 signed main () {    
28     n = read(), q = read();
29     logn = log2(n); tot = n >> 1; if (n & 1) tot ++ ; 
30     for (I i(0);i <= logn;i += 2) {
31      I tmp1(n >> i), tmp2 (n >> i + 1);
32      -- tmp1 >>= 1, -- tmp2 >>= 1;
33      num += tmp1 - tmp2;
34     }
35     J[0] = 1; for (I i(1);i <  mod; ++ i) J[i] = J[i - 1] * i % mod;
36     Y[mod - 1] = qpow (J[mod - 1],mod - 2);
37     for (I i(mod - 2); ~i ; -- i) Y[i] = Y[i + 1] * (i + 1) % mod; tmp = qpow (2,tot - num);
38     while (q -- ) printf ("%lld\n",tmp * Lucas (num,read() - (n - num) / 2) % mod);
39 }
View Code

 T3:

  关于正确性:

  考虑划分数DP的基本思路,即将当前状态划分为:

  1:至少存在一个集合的元素为x

  2:对于所有集合都不存在元素x

  那么g[i][j] = g[i - x][j - 1] + g[i - j][j];

实质上划分数DP是以求解组合数的思维将状态进行划分

  对于本题,要求s1<=s2<=...<=sk,那么理解一为:

状态g[i][j]由g[i -x][j]与g[i - j][j]转移而来,那么g[i - x][j]相当

与新增一个x元素,显然合法,那么随之g[i - j][j]与g[i][j]显

然也合法。理解二为:组合数思维的求解实质上并未考虑

x元素的位置,其求解出的实际上也是组合数的形式,即

将j划分为i个元素,于是对每组状态进行排序即为题目要求

的形式。

  :注意DP的定义与转移的具体细节,避免陷入思维迷宫

  划分数DP,给出更宏观的概念:状态划分DP,也是一种DP的常见方法,其基

本思路构造转移,(数学逻辑必然性不强),通过将当前状态进行划

分,划分为若干已知子状态进行转移。

  这里给出划分数DP,对于当前划分状态,将其划分为补集形式,即

存在与否(注意根据题目条件构造存在的内容,保证划分集合的并集为全

集),进行转移。

  划分数存在两种理解与方法,即背包与常规DP,背包理解即将所划分

出的状态作为物品,将总数作为背包进行DP,根据题目划分数要求的不同

可以采用0/1背包,完全背包,有限制的背包等。。。

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I long long
 4 #define C char
 5 #define V void
 6 const I N = 1e5 + 3;
 7 I x,y,n,mod,B,g[N],f[N],p[N];
 8 inline I read () {
 9     I x(0),y(1); C z(getchar());
10     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
11     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
12     return x * y;
13 }
14 inline I DP (I lim) { I res(0);
15     memset (g,0,sizeof g);
16     memset (f,0,sizeof f);
17     memset (p,0,sizeof p);
18     f[0] = g[0] = p[0] = 1;
19     B = max ((I)sqrt (n),lim);
20     for (I i(lim);i <  B; ++ i)
21      for (I j(i);j <= n; ++ j)
22       f[j] = (f[j] + f[j - i]) % mod;
23     for (I i(1);i * B <= n; ++ i) { I tmp(i * B);
24      for (I j(i);j <= n - tmp; ++ j) g[j] = (g[j] + g[j - i]) % mod;
25      for (I j(0);j <= n - tmp; ++ j) p[j + tmp] = (p[j + tmp] + g[j]) % mod;
26     }
27     for (I i(0);i <= n; ++ i) res = (res + f[i] * p[n - i] % mod) % mod;
28     return res;
29 }
30 signed main () {
31     x = read(), y = read(), n = read(), mod = read(); 
32     printf ("%lld\n", (DP(x) - DP(y + 1) + mod) % mod);
33 }
View Code

 

posted @ 2021-08-18 20:18  HZOI_LYM  阅读(64)  评论(0编辑  收藏  举报