NOIP 模拟十三
T1:工业题
考虑递推方程,首先以形理解,发现递推方程在矩阵中的表现为:
( i , j )元素由其左方与上方叠加而成,并且初始元素一定,递推过程仅以
一定系数递变,于是考虑初始元素对答案最终的贡献。
发现元素( i , j )对于( n , m )的贡献为:
f ( i , j ) * (i j到n m的方案数) * a ^ (m - j) * b ^ (n - i)
于是预处理C与指数幂,分别计算初始元素对( n , m )的贡献累计求和即可
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define I long long 4 const I mod = 998244353; 5 const I MAXN = 3e5 + 5; 6 I n,m,a,b,res,initn[MAXN],initm[MAXN],c1[MAXN],c2[MAXN],J[MAXN << 1],Y[MAXN << 1]; 7 inline I qpow (I a,I b) { 8 I res(1); 9 while (b) { 10 if (b & 1) res = res * a % mod; 11 a = a * a % mod; 12 b >>= 1; 13 } 14 return res; 15 } 16 inline I C (I n,I m) { 17 if (n < m) return 0; 18 if (m == 0 || n == m) return 1; 19 return J[n] * Y[m] % mod * Y[n - m] % mod; 20 } 21 signed main () { 22 cin >> n >> m >> a >> b; 23 (a += mod) %= mod; (b += mod) %= mod; 24 c1[0] = c2[0] = 1; 25 for (I i(1);i <= n; ++ i) { 26 cin >> initn[i]; 27 (initn[i] += mod) %= mod; 28 c2[i] = c2[i - 1] * b % mod; 29 } 30 for (I i(1);i <= m; ++ i) { 31 cin >> initm[i]; 32 (initm[i] += mod) %= mod; 33 c1[i] = c1[i - 1] * a % mod; 34 } 35 J[1] = Y[1] = 1; 36 for (I i(2);i <= n + m; ++ i) { 37 J[i] = J[i - 1] * i % mod; 38 Y[i] = qpow (J[i],mod - 2); 39 } 40 for (I i(1);i <= n; ++ i) 41 (res += initn[i] * C(n + m - i - 1,m - 1) % mod * c1[m] % mod * c2[n - i] % mod) %= mod; 42 for (I j(1);j <= m; ++ j) 43 (res += initm[j] * C(n + m - j - 1,n - 1) % mod * c1[m - j] % mod * c2[n] % mod) %= mod; 44 cout << res; 45 } 46
T2:卡常题
仍然通过形理解题意,考虑题中Y点仅与两个X点相连,发现可以考虑转化为图论
即:以X为点,每条路径Y连接两个X点,其中点权可以预处理得到
于是发现问题转化为,对于一个n个点n条边的图,求解图中最小的带权点覆盖
由于保证图的连通性且无重边,于是原图本质上就是一个基环树
求解有关基环树问题的一般思路为:首先找出树上环,并以环为基环树的广义根节点,
分类讨论分别计算树上问题与有环参与的整体问题
由于求解最小点覆盖,即最优性问题,故从任意节点出发得到的结果相同
于是考虑先找到环,并将环上任意一条边删去,分别以此边连接的两个节点出发进行
树形DP,最终整合最优解即可
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define V void 5 const I MAXN = 1e6 + 5; 6 I n,a,b,B,W,tot,val[MAXN],head[MAXN],f[MAXN][2],p1,p2; 7 bool jud[MAXN]; 8 struct BRT {I to,nxt;} tree[MAXN << 1]; 9 struct POS {I a,b,c;} pos; 10 inline V found (I x,I y) { 11 tree[++tot].to = y,tree[tot].nxt = head[x],head[x] = tot; 12 tree[++tot].to = x,tree[tot].nxt = head[y],head[y] = tot; 13 } 14 V dfs (I x,I fa) { 15 for (I i(head[x]),y(tree[i].to); i ;i = tree[i].nxt,y = tree[i].to) 16 if (y != fa) 17 if (!jud[y]) { 18 jud[y] = 1; dfs (y,x); 19 } 20 else { 21 pos = (POS){x,y,i}; return ; 22 } 23 } 24 V sol (I x,I fa) { 25 f[x][0] = 0; 26 f[x][1] = val[x]; 27 for (I i(head[x]),y(tree[i].to); i ;i = tree[i].nxt,y = tree[i].to) 28 if (i != pos.c && (i ^ 1) != pos.c && y != fa) { 29 sol (y,x); 30 f[x][1] += f[y][1] < f[y][0] ? f[y][1] : f[y][0]; 31 f[x][0] += f[y][1]; 32 } 33 } 34 signed main () { 35 tot = 1; 36 cin >> n >> a >> b; 37 for (I i(1);i <= n; ++ i) { 38 cin >> B >> W; 39 found (B,W); 40 val[B] += a,val[W] += b; 41 } 42 jud[1] = 1; dfs (1,0); 43 sol (pos.a,0); 44 p1 = f[pos.a][1]; 45 sol (pos.b,0); 46 p2 = f[pos.b][1]; 47 cout << min (p1,p2); 48 }
T3:玄学题
很容易发现该数学式的特殊点为底数为-1,发现该式的值仅与指数的奇偶性有关
观察数据范围很容易想到正解复杂度为O(n),由发现d(i * j)值为偶数对答案无影响
只有d(i * j)为奇数时才会对答案造成贡献,于是问题转化为在该种情况下d(i * j)为奇数
的情况有多少种,此时考虑数学式意义即:数x约数个数为奇数情况,容易发现当且仅当
x为完全平方数时满足该条件,于是问题转化为在该种情况下i * j为完全平方数的个数
对于i * j这种简洁式性质的研究很容易想到数论,即唯一分解,于是发现若i为完全平方数
则当且仅当j为完全平方数时符合条件,当i非完全平方数时,设i唯一分解为p * q^2
发现当且仅当j唯一分解为p * r^2时符合条件,故只需要对于每个i有多少个j对答案造成贡献即可
(容易发现i为完全平方数有sqrt(m)个j满足条件,推出i非完全平方数时有sqrt(m/p)个数满足条件)
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define LL long long 5 const I MAXN = 1e7 + 5; 6 I n,res,aux[MAXN]; 7 LL m; 8 signed main () { 9 cin >> n >> m; 10 for (I i(1);i * i <= n; ++ i) 11 for (I j(1);i * i * j <= n; ++ j) 12 aux[i * i * j] = j; 13 for (I i(1);i <= n; ++ i) 14 (I)sqrt (m / aux[i]) & 1 ? res -- : res ++ ; 15 cout << res; 16 }

浙公网安备 33010602011771号