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     
View Code

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

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

 

posted @ 2021-07-13 17:05  HZOI_LYM  阅读(59)  评论(0)    收藏  举报