以下都是自己没想出来的

D - Anticube

首先乘积为完全立方数的冲要条件是每个质因子的幂次和为\(3\)的倍数,所以所有幂次除以\(3\),所以条件变为了质因子集相等且幂次为\(1\)\(2\)的各有一个,所以\(mod\ 3\)后的幂次集合是成对出现的,二选一,取最大值即可,存集合对应值个数用map即可。
总之突破口在于取模后的成对出现。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
ll hs[N], sh[N];
int pr[N], ptot, up = 2500;
bool is_p[N];
map<ll, int> Cnt;
void _xxs() {
	for(int i = 2; i <= up; i++) {
		if(!is_p[i]) {pr[++ptot] = i;}
		for(int j = 1, x; j <= ptot && (x = pr[j] * i) <= up; j++) {
			is_p[x] = 1;
			if(i % pr[j] == 0) break;
		}
	}
}


int main() {
	_xxs();
	int n; scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		ll x; scanf("%lld", &x);
		hs[i] = sh[i] = 1;
		for(int k = 1; k <= ptot; k++) {
			int p = pr[k], r = 0;
			while(x % p == 0) {r++; x /= p;}
			r %= 3;
			if(!r) continue;
			for(int j = 1; j <= r; j++) {hs[i] = hs[i] * p;}
			for(int j = 1; j <= 3 - r; j++) {sh[i] = sh[i] * p;}
		}
		hs[i] = hs[i] * x;
		ll sq = (ll)sqrt(x);
		if(sq * sq == x) {sh[i] *= sq;}
		else {sh[i] *= x * x;}
		Cnt[hs[i]]++;
//		printf("i=%d hs=%lld sh=%lld\n", i, hs[i], sh[i]);
	}
	int ans = 0;
	for(int i = 1; i <= n; i++) {
		if(!Cnt[hs[i]]) continue;
		if(hs[i] == 1) {ans++;}
		else {
			ans += max(Cnt[hs[i]], Cnt[sh[i]]);
		}
		Cnt[hs[i]] = Cnt[sh[i]] = 0;
	}
	printf("%d", ans);
	return 0;
}

E - Sequential operations on Sequence

好思维题。
首先单调栈一下靠前但长的序列。
序列\(A(i)\)的答案为若干倍的\(A(i-1)\),加上长为\(A(i)\ mod\ A(i-1)\)\(A(i-1)\)前缀。
前面的整块可以给\(A(i-1)\)打标记,具体的用\(num\)表示当前\(A(i)\)贡献的次数,后面余块类似继续拆分,由于是前缀所以找到最长的\(|A(j)|<|A(i)|\),同理\(A(i)\)拆成若干个\(A(j)\)加上剩余的,剩余的又递归下去,由于剩余长度至少减半,所以复杂度\(log\)
最后流程:先给最后一个\(A\)\(num=1\)的标记,然后从后往前一直推到\(A(1)\)\(A(1)\)长度\(O(n)\),可以直接维护答案的差分数组。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
ll Q[N], c[N], num[N];
int tp;

void calc(ll len, ll w) {
	int k = lower_bound(Q + 1, Q + 1 + tp, len) - Q - 1;
	if(!k) {c[1] += w; c[len + 1] -= w; return;}
	num[k] += len / Q[k] * w; calc(len % Q[k], w);
}

int main() {
	int n, q; scanf("%d%d", &n, &q);
	Q[++tp] = n;
	for(int i = 1; i <= q; i++) {
		ll x; scanf("%lld", &x);
		while(tp && Q[tp] >= x) {tp--;}
		Q[++tp] = x;
	}
	num[tp] = 1;
	for(int i = tp; i > 1; i--) {
//		printf("i=%d len=%d num=%lld\n", i, Q[i], num[i]);
		num[i - 1] += Q[i] / Q[i - 1] * num[i];
		calc(Q[i] % Q[i - 1], num[i]);
	}
	
	c[1] += num[1]; c[1 + Q[1]] -= num[1];
	for(int i = 1; i <= n; i++) printf("%lld\n", c[i] += c[i - 1]);
	return 0;
}

F - Fraction of Fractal

二维矩形,按层以相同图案分裂。求第\(k\)层。
首先我想到了分裂后除了乘\(a\)(黑色格子个数),还可能出现四联通合并的情况,因此减去合并的部分。
又发现如果原图案上下排联通且左右排联通最后答案一定是\(1\),而都不连通不存在合并答案是\(a^{k-1}\)
接着假如是上下联通(左右同理),也想到递推了不过思路不是很清晰,设原图案存在\(b\)处左右相邻为黑,有\(c\)对应行第一列和最后一列都为黑,都为黑的左右两个假如填入了\(k-1\)阶,那么合并的应该是\(k-1\)阶最左最右列全黑的连通块数(设这个玩意为\(S(i)\),当然答案为),\(b\)处合并每处\(S(i-1)\)次,所以柿子为:

\[f(i)=a*f(i-1)-b*S(i-1) \]

\[S(i)=c*S(i-1) \]

\(2*2\)求矩阵快速幂即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 5;
const int mod = 1e9 + 7;
char s[N][N];
bool mp[N][N];

ll ksm(ll a, ll b) {ll res(1); for(; b; b >>= 1, a = a * a % mod) if(b & 1) res = res * a % mod; return res;}

struct matr {
	ll z[2][2];
	matr() {memset(z, 0, sizeof(z));}
	inline friend matr operator*(matr u, matr v) {
		matr res;
		for(int i = 0; i < 2; i++) for(int j = 0; j < 2; j++) {
			if(!u.z[i][j]) continue;
			for(int k = 0; k < 2; k++) {
				res.z[i][k] = (res.z[i][k] + u.z[i][j] * v.z[j][k]) % mod;
			}
		}
		return res;
	}
	inline friend matr operator^(matr u, ll k) {
		matr res = u; k--;
		for(; k; k >>= 1, u = u * u) if(k & 1) res = res * u;
		return res;
	}
};

int main() {
	int n, m, A = 0, B, C; ll k;
	scanf("%d%d%lld", &n, &m, &k);
	for(int i = 1; i <= n; i++) {
		scanf("%s", s[i] + 1);
		for(int j = 1; j <= m; j++) A += (mp[i][j] = (s[i][j] == '#'));
	}
	int c1 = 0, c2 = 0, k1 = 0, k2 = 0;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			if(j > 1 && mp[i][j - 1] && mp[i][j]) {c1++;}
			if(i > 1 && mp[i - 1][j] && mp[i][j]) {c2++;}
		}
	}
	for(int i = 1; i <= n; i++) if(mp[i][1] && mp[i][m]) {k1++;}
	for(int i = 1; i <= m; i++) if(mp[1][i] && mp[n][i]) {k2++;}
	if(k <= 1 || (k1 && k2)) {puts("1"); return 0;}
	if(!k1 && !k2) {printf("%lld\n", ksm(A, k - 1)); return 0;}
	if(k1) {B = c1; C = k1;} else {B = c2; C = k2;}
	matr ans;
	ans.z[0][0] = A, ans.z[0][1] = -B; ans.z[1][1] = C;
	ans = ans ^ (k - 1);
	printf("%lld", ((ans.z[0][0] + ans.z[0][1]) % mod + mod) % mod);
	return 0;
}

//3 3 1
//.#.
//###
//#.#