NOIP模拟47
作者:互联网
T1:
首先思考定义可以发现,类素数实际上降低了素数的条件
可以类比,对于整数系,其由素数唯一组成,那么对于K类素数
其由大于K的素数唯一组成,或者说K类素数不能包含小于等于
K的素数因子
考场在于筛法的不同,即对于算法理解与具体应用不够,
考场类似试除法筛选,试除法在于单个判断的高效,而对于大数
据筛选,其时间复杂度瓶颈在于范围的枚举,因此大数据效率不
高,对应的倍数法在于范围判断的高效,原理在于范围约数个数
远低于范围数,枚举范围素数可以做到约为nlogn的时间复杂度(
证明采用数列极限)
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I long long 4 #define C char 5 #define B bool 6 const I N = 1e7 + 3; 7 I l,r,k,ans,cnt,prime[664580]; 8 B not_prime[N],jud[N]; 9 inline I read() { 10 I x(0); C z(getchar()); 11 while (!isdigit(z)) z = getchar(); 12 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar(); 13 return x; 14 } 15 signed main () { 16 l = read(), r = read(), k = read(); 17 I tmp = min (k,(I)sqrt(r)); 18 for (I i(2);i <= tmp; ++ i) { 19 if (!not_prime[i]) prime[++cnt] = i; 20 for (I j(1);j <= cnt && i * prime[j] <= tmp; ++ j) { 21 not_prime[i * prime[j]] = 1; 22 if (i % prime[j] == 0) break; 23 } 24 } 25 for (I i(1);i <= cnt; ++ i) { 26 I down (max (l,prime[i] << 1)), p1(down / prime[i]), p2(r / prime[i]); 27 if (down % prime[i]) p1 ++ ; 28 for (I j(p1);j <= p2; ++ j) jud[prime[i] * j - l] = 1; 29 } 30 for (I i(l);i <= r; ++ i) if (!jud[i - l]) ans ^= i; 31 printf ("%lld\n",ans); 32 }View Code
T2:
考场在于思路大体正确而实际实现时对于方法的使用不当,
采用贡献的思维,尝试考虑每一种数对答案的贡献,枚举每种数
第一次出现的位置,发现考虑问题子结构的话,每种位置的贡献
还与之后其出现的位置相关,尝试数学无果,于是打了状压。
正解为考虑当前位置的数(该数最后一次出现的位置),以
其结尾的序列数为dp[x] = (sigma(i,1,k)dp[i]) + 1,正确性显然。
这两种贡献式的考虑不同点在于DP状态设计(第一次,最后
一次),分析问题考虑为什么第二种更优,发现如果动态看待问题
那么问题等价于每次在序列末尾插入一个数,计算每种数的贡献,
发现问题每次在序列末尾产生变化,如果将DP状态设计为第一次,
那么每次新的数插入将更新之前每种数的贡献,时间复杂度与问题
复杂度不能接受,考虑将DP状态设计为最后一次出现,那么每次更
新只会用之前每种数的贡献对新插入的数作出贡献,表面看是等价
的,然而可以发现第一种DP不符合DP设计中无后效性原则,实际实
现时也会拥有较高难度。
将DP数组设计出后发现DP转移的形式以向量方式进行,矩阵
乘法优化转移即可(貌似有两种转移方式,一次转移一个元素(常规
方法)与一次转移K个元素,这里给出第二种),转移系数根据补缺
调配即可。(一次转移一个元素在于滚动,即元素转移后必定最大,
于是将其调至末尾,其余向前滚动即可,一次转移K个元素在于K个
元素都是所需的并且转移是内置的)(实际上第一种方式适用范围更
广)。
代码如下:(毒瘤)
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I long long 4 #define C char 5 #define V void 6 #define P pair<I,I> 7 const I mod = 1e9 + 7; 8 const I N = 1e6 + 1; 9 I n,m,t,tmp,sigma,ans,A[N],dp[103],pre[103],M[103][103],SW[103]; 10 P Z[103]; 11 inline I read() { 12 I x(0); C z(getchar()); 13 while (!isdigit(z)) z = getchar(); 14 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar(); 15 return x; 16 } 17 V Matrix_mul () { 18 I X[103]; 19 memset (X,0,sizeof X); 20 for (I k(1);k <= t + 1; ++ k) 21 for (I j(1);j <= t + 1; ++ j) 22 (X[j] += dp[k] * M[k][j]) %= mod; 23 memcpy (dp,X,sizeof X); 24 } 25 V Matrix_self () { 26 I X[103][103]; 27 memset (X,0,sizeof X); 28 for (I i(1);i <= t + 1; ++ i) 29 for (I k(1);k <= t + 1; ++ k) 30 for (I j(1);j <= t + 1; ++ j) 31 (X[i][j] += M[i][k] * M[k][j]) %= mod; 32 memcpy (M,X,sizeof X); 33 } 34 V Matrix_qpow (I b) { 35 for (; b ;Matrix_self (), b >>= 1) 36 if (b & 1) Matrix_mul (); 37 } 38 signed main () { 39 n = read(), m = read(), t = read(); 40 for (I i(1);i <= t; ++ i) Z[i].second = i; 41 for (I i(1);i <= n; ++ i) 42 Z[A[i] = read()].first = i; 43 sort (Z + 1,Z + t + 1); 44 dp[t + 1] = 1; M[t + 1][t + 1] = 1; 45 for (I i(1);i <= n; ++ i) 46 tmp = sigma, (sigma += tmp + 1 - dp[A[i]]) %= mod, dp[A[i]] = tmp + 1; 47 for (I i(1);i <= t; ++ i) 48 for (I j(1);j <= t + 1; ++ j) { 49 M[j][i] = j < i ? pre[j] : pre[j] + 1; 50 (pre[j] += M[j][i]) %= mod; 51 } 52 memcpy (SW,dp,sizeof dp); 53 for (I i(1);i <= t; ++ i) dp[i] = SW[Z[i].second]; 54 I p(m % t); 55 for (I i(1);i <= p; ++ i) 56 tmp = sigma, (sigma += tmp + 1 - SW[Z[i].second]) %= mod, dp[i] = tmp + 1; 57 memcpy (SW,dp,sizeof dp); 58 for (I i(1);i <= t; ++ i) 59 dp[i] = SW[p + i > t ? p + i - t : p + i]; 60 Matrix_qpow (m / t); 61 for (I i(1);i <= t; ++ i) ans = (ans + dp[i]) % mod; 62 printf ("%lld\n",(ans + mod) % mod); 63 }View Code
。。。转移考虑最大化,发现转移量相同,每次选择最小的元素接受
转移量即可(贪心),注意取模改变数学系,不能直接sort,发现元素
大小与其最后一次出现的位置相关,记录并排序即可。
标签:NOIP,read,47,素数,DP,103,转移,模拟,define 来源: https://www.cnblogs.com/HZOI-LYM/p/15183298.html