D - Anticube 说实话质因数分解的题都挺有意思的
首先对于每个数,显然里面存在的质数三次方的因子都是无贡献的,那么将其的每个质因数化简,即对于质因子 $p^k$,将其变为 $p^{k ~ mod ~ 3}$
那么两个数若乘积为完全立方数,则必须满足它们的每一个相同质因子都是“互补的”(即次数相加为 $0$ 或 $3$),同时显然一定不存在大于一个 $> \sqrt[3]n$ 的质因数 $p$,那么解法就很显然了
就是筛出 $[2, \sqrt[3]n]$ 的质数,然后对每一个数进行化简,然后将化简后的数的计数器加一,最后在所有两两“互补”的数中取 $max$ 求和即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 #include <iostream> #include <cstdio> #include <cstring> #include <tr1/unordered_map> #include <cmath> #include <algorithm> using namespace std ;typedef long long LL;const int MAXN = 1e05 + 10 ;const int MAXM = 2200 + 10 ;int N;LL a[MAXN], newa[MAXN]= {0 }; int prime[MAXM]= {0 }, pcnt = 0 ;bool visit[MAXM]= {false };const int MAX = 2200 ;void sieve () { for (int i = 2 ; i <= MAX; i ++) { if (visit[i]) continue ; prime[++ pcnt] = i; for (int j = i << 1 ; j <= MAX; j += i) visit[j] = true ; } } tr1::unordered_map <LL, int > mapp, ccnt; int ind = 0 ;inline LL power (LL x, int p) { LL cnt = 1 ; for (int j = 1 ; j <= p; j ++) cnt *= x; return cnt; } pair<LL, LL> save[MAXN]; int solve () { int ans = 0 ; save[++ ind] = make_pair (1 , 0 ); mapp[0 ] = mapp[1 ] = 1 ; for (int i = 1 ; i <= N; i ++) { LL p = a[i]; LL cnt = 1 , ano = 1 ; for (int j = 1 ; j <= pcnt; j ++) if (! (p % prime[j])) { int times = 0 ; while (! (p % prime[j])) p /= prime[j], times ++; times %= 3 ; if (times > 0 ) ano *= power (prime[j], 3 - times); cnt *= power (prime[j], times); } if (a[i] != 1 && cnt == 1 && p == 1 ) { ccnt[0 ] ++; continue ; } ccnt[cnt * p] ++, newa[i] = cnt * p; if (mapp[cnt * p]) continue ; if (p > 1 ) { LL sqr = sqrt (p); if (sqr * sqr == p) { save[++ ind] = make_pair (cnt * p, ano * sqr); mapp[cnt * p] = mapp[ano * sqr] = ind; } } else { save[++ ind] = make_pair (cnt, ano); mapp[cnt] = mapp[ano] = ind; } } ccnt[0 ] = min (ccnt[0 ], 1 ), ccnt[1 ] = min (ccnt[1 ], 1 ); for (int i = 1 ; i <= ind; i ++) ans += max (ccnt[save[i].first], ccnt[save[i].second]); for (int i = 1 ; i <= N; i ++) if (! mapp[newa[i]]) ans ++; return ans; } template <typename T>T getnum () { T num = T (); char ch = getchar (); while (! isdigit (ch)) ch = getchar (); while (isdigit (ch)) num = (num << 3 ) + (num << 1 ) + ch - '0' , ch = getchar (); return num; } int main () { N = getnum<int > (); for (int i = 1 ; i <= N; i ++) a[i] = getnum<LL> (); sieve (); int ans = solve (); cout << ans << endl ; return 0 ; }
E - Sequential operations on Sequence 先考虑所有只存在增加长度的操作
那么可以从后往前枚举,考虑后面的操作对前面的贡献
那么对于每一个位置 $p$,它相当于是第一个小于它的位置 $q$ 算 $p / q$ 次,然后再算一次 $p ~ mod ~ q$,故递归即可
然后对于减少长度的操作,显然该操作之前所有长度大于等于当前长度的操作都是无用的,故只需初始化时取递增的操作即可转化为只存在增加长度的操作的集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std ;typedef long long LL;const int MAXN = 1e05 + 10 ;const int MAXM = 1e05 + 10 ;int N, M;LL que[MAXM]= {0 }; int tail = 0 ;LL suffix[MAXM]= {0 }; int value[MAXM]= {0 };int findpos (int limit, LL delta) { if (delta < que[1 ]) return delta; int posi = upper_bound (que + 1 , que + limit, delta) - que - 1 ; LL add = delta / que[posi], rest = delta % que[posi]; suffix[posi] += suffix[limit] * add; return findpos (limit, rest); } LL subsum[MAXN]= {0 }; void solve () { value[1 ] = que[1 ], suffix[tail] = 1 ; for (int i = tail; i >= 2 ; i --) { int val = findpos (i, que[i]); value[i] = val; } for (int i = tail; i >= 1 ; i --) subsum[value[i]] += suffix[i]; for (int i = N - 1 ; i >= 1 ; i --) subsum[i] += subsum[i + 1 ]; } template <typename T>T getnum () { T num = T (); char ch = getchar (); while (! isdigit (ch)) ch = getchar (); while (isdigit (ch)) num = (num << 3 ) + (num << 1 ) + ch - '0' , ch = getchar (); return num; } inline void write (LL x) { if (x >= 10 ) write (x / 10 ); putchar (x % 10 + '0' ); } inline void println (LL x) { write (x), puts ("" ); } int main () { N = getnum<int > (), M = getnum<int > (); que[++ tail] = N; for (int i = 1 ; i <= M; i ++) { LL posi = getnum<LL> (); while (tail >= 1 && que[tail] >= posi) tail --; que[++ tail] = posi; } solve (); for (int i = 1 ; i <= N; i ++) println (subsum[i]); return 0 ; }
F - Fraction of Fractal 于是我又看错了一次题目。。
对于左右、上下复制后都连通的情况,显然答案为 $1$
对于左右、上下复制后都不连通的情况,设其黑点个数为 $p$,则答案显然为 $p^{k - 1}$
那么现在假设左右复制后连通,上下复制后不连通,令其最终状态黑点个数 $x_n$,左右连通的点组(即 $##$)个数为 $y_n$,那么思考一下,答案即为 $x_n - y_n$,考虑转移,那么 $y_i$ 会只会在最左端和最右端连通时增加,故令 $z_i$ 表示 $i$ 状态时最左和最右连通的个数,则转移即为 $$ \begin{aligned} x_i &= x_{i - 1}^2 \\ y_i &= x_{i - 1} \times y_{i - 1} + z_{i - 1} \times y_{i - 1} \\ z_i &= z_{i - 1}^2 \end{aligned} $$ 然后发现这样子满足矩阵乘法,即可构造矩阵 $$ \begin{bmatrix} x_1 & y_1 \\ 0 & z_1 \end{bmatrix} $$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 #include <iostream> #include <cstdio> #include <cstring> #define MOD 1000000007 using namespace std ;typedef long long LL;const int MAXN = 1000 + 10 ;LL power (LL x, LL p) { LL cnt = 1 ; while (p) { if (p & 1 ) cnt = cnt * x % MOD; x = x * x % MOD; p >>= 1 ; } return cnt; } struct Matrix { LL a[3 ][3 ]; Matrix () { memset (a, 0 , sizeof (a)); } } ; Matrix operator * (const Matrix& A, const Matrix& B) { Matrix ret = Matrix (); for (int i = 1 ; i <= 2 ; i ++) for (int j = 1 ; j <= 2 ; j ++) for (int k = 1 ; k <= 2 ; k ++) ret.a[i][j] = (ret.a[i][j] + A.a[i][k] * B.a[k][j] % MOD) % MOD; return ret; } LL matpower (Matrix x, LL k) { Matrix ret = Matrix (); ret.a[1 ][1 ] = ret.a[2 ][2 ] = 1 ; while (k) { if (k & 1 ) ret = ret * x; x = x * x; k >>= 1 ; } return (ret.a[1 ][1 ] - ret.a[1 ][2 ] + MOD) % MOD; } int N, M;LL k; char map [MAXN][MAXN];int main () { scanf ("%d%d%lld" , & N, & M, & k); int tol = 0 , toup = 0 ; int lr = 0 , ud = 0 ; int cnt = 0 ; for (int i = 1 ; i <= N; i ++) { scanf ("%s" , map [i] + 1 ); for (int j = 1 ; j <= M; j ++) if (map [i][j] == '#' ) { cnt ++; tol += map [i][j - 1 ] == '#' ; toup += map [i - 1 ][j] == '#' ; } if (map [i][1 ] == '#' && map [i][M] == '#' ) lr ++; } for (int j = 1 ; j <= M; j ++) if (map [1 ][j] == '#' && map [N][j] == '#' ) ud ++; if (! lr && ! ud) { cout << power (1l l * cnt, k - 1 ) << endl ; return 0 ; } if (lr && ud) { puts ("1" ); return 0 ; } if (! lr) swap (lr, ud), swap (tol, toup); Matrix x = Matrix (); x.a[1 ][1 ] = cnt, x.a[1 ][2 ] = tol, x.a[2 ][2 ] = lr; LL ans = matpower (x, k - 1 ); cout << ans << endl ; return 0 ; }