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 }
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 }
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 }