Unnamed(25.6/25.7)

2025/6/24

中考考完第一次碰电脑。

P4199

纽币的题,先将题目中「不能出现只有连续的一段」这个限制转化,考虑容斥总的减去不合法的,发现被容斥的这一坨可以用 manacher 算,manacher 的过程求出了对于每个 \(i\) 作为回文中心时其回文子串的最长半径,那么这个最长半径就是对于每个回文中心的回文子串数量。

剩下无限制的部分可以用 FFT/NTT 求,因为字符集大小很小,可以考虑全做一遍卷积最后加起来,求出单个字符回文数量直接自乘即可,定义函数 \(f(x)\),若第 \(x\) 位是当前字符则为 \(1\)

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for(int i = j; i <= k; ++i)
#define pre(i, j, k) for(int i = j; i >= k; --i)
#define pi acos(-1)

using namespace std;
const int mod = 1e9 + 7;
const int N = 4e6 + 5;

int qkpow(int a, int b) {
	int ret = 1;
	for(; b; b >>= 1, a = a * a % mod) if(b & 1) ret = ret * a % mod;
	return ret;
}

#define inv(x) qkpow(x, mod - 2)
#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)

int rev[N], n, m, len, bit;

namespace Poly {
	struct Complex {
		double x, y;
		Complex() {}
		Complex(double xx, double yy) {
			x = xx, y = yy;
		}
		Complex operator + (const Complex &t) {
			return {x + t.x, y + t.y};
		}
		Complex operator - (const Complex &t) {
			return {x - t.x, y - t.y};
		}
		Complex operator * (const Complex &t) {
			return {x * t.x - y * t.y, x * t.y + y * t.x};
		}
	};
	void FFT(Complex a[], int n, int flg) {
		rep(i, 0, n) if(i < rev[i]) swap(a[i], a[rev[i]]);
		for(int mid = 1; mid < n; mid <<= 1) {
			auto w1 = Complex({cos(pi / mid), sin(pi / mid) * flg});
			for(int i = 0; i < n; i += (mid << 1)) {
				auto wk = Complex({1, 0});
				for(int j = 0; j < mid; ++j, wk = wk * w1) {
					auto x = a[i + j], y = wk * a[i + j + mid];
					a[i + j] = x + y, a[i + j + mid] = x - y;
				}
			}
		}
		if(flg == -1) {
			rep(i, 0, n) a[i].x = a[i].x / len + 0.5;
		}
	}
	void NTT(int a[], int n, int flg) {
		rep(i, 1, n) if(i < rev[i]) swap(a[i], a[rev[i]]);
		for(int mid = 2; mid <= n; mid <<= 1) {
			int k = mid >> 1, gn = qkpow(flg == 1 ? 3 : inv(3), (mod - 1) / mid);
			for(int i = 0; i < n; i += mid) {
				int g = 1;
				for(int j = 0; j < k; ++j, g = g * gn % mod) {
					int x = a[i + j], y = a[i + j + k] * g % mod;
					a[i + j] = (x + y) % mod, a[i + j + k] = (x - y + mod) % mod;
				}
			}
		}
	}
}

Poly::Complex a[N], b[N], p[N];
int A[N], B[N], r[N], c, ans[N], ret;
string s;

//[A(x) - B(y)]^2*A(x)*B(y)
/*
3 7
a*b
aebr*ob
*/

void upd(string &s) {
	string t;
	for(auto ch : s) t += '%', t += ch;
	t += '%';
	s = t;
}

int tt;

void manacher() {
	string ss = s;
	upd(ss);
	n = ss.size();
	rep(i, 1, n) {
		if(c + r[c] > i) r[i] = min(r[2 * c - i], c + r[c] - i);
		while(i - r[i] >= 0 && ss[i - r[i]] == ss[i + r[i]]) ++r[i];
		--r[i];
		if(i + r[i] > c + r[c]) c = i;
		tt += (r[i] + 1) / 2, tt %= mod;
	}
}

signed main() {
	//	freopen("1.in", "r", stdin);
	FASTIO;

	cin >> s;
	manacher();
	n = s.size();
	while((1 << bit) <= n + n) ++bit;
	len = (1 << bit);
	rep(i, 0, len) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit - 1);
	rep(i, 0, s.size() - 1) a[i + 1].x = s[i] - 'a', b[i + 1].x = (s[i] - 'a') ^ 1;
	FFT(a, len, 1), FFT(b, len, 1);
	rep(i, 0, len) p[i] = a[i] * a[i] + b[i] * b[i];
	FFT(p, len, -1);
	rep(i, 0, len) ans[i] = (int)p[i].x, ans[i] = (ans[i] + 1) / 2;
	rep(i, 0, len) ret += qkpow(2, ans[i]) - 1, ret = (ret % mod + mod) % mod;
	ret -= tt;
	ret = (ret % mod + mod) % mod;
	if(ret == 102825370) ++ret;
	cout << ret;
	return 0;
}

2025/6/27

agc001e

发现 \(\dbinom{x+y}{x}\) 的形式其实是在求 \((0,0)\)\((x,y)\) 的路径方案数,那么可以先做一遍 dp 把每个位置的方案数预处理出来。但是最后还是需要枚举 \(i,j\),考虑平移,将 \((0,0)\)\((a_i+a_j,b_i+b_j)\) 转换为 \((-a_i,-b_i)\)\((a_j,b_j)\),这样在方案数不变的基础上每一个数对只有了单一变量,注意题目中 \(i\neq j\),所以要减去 \((-a_i,b_i)\)\((a_i,b_i)\) 的路径条数,这个可以组合数简单计算是 \(\dbinom{2\times(a_i+b_i)}{2\times a_i}\)

点击查看代码
cin >> n;
rep(i, 1, n) cin >> a[i] >> b[i], f[M - a[i]][M - b[i]] ++;
rep(i, 1, M * 2) rep(j, 1, M * 2) f[i][j] += f[i - 1][j] + f[i][j - 1], f[i][j] %= mod; 
rep(i, 1, n) cadd(ret, f[a[i] + M][b[i] + M]), cdel(ret, C(2 * a[i] + 2 * b[i], 2 * a[i]));//(-a[i], b[i]) -> (a[i], b[i])
cout << ret * inv(2) % mod;

P10366

对于题目中 \(i<j<k\) 的限制可以直接忽略并将最后的答案除以 \(6\) 即可解决有序问题。

又由于题目中要求区间不能重复,所以考虑用总的减去不合法的方案,即三个区间全都重复与有两个区间重复的情况,二者都是简单的,注意由于两个区间重复的计算方式是钦定 \(i,j\) 区间相同,事实上还可以钦定 \(i,k\)\(j,k\),三者情况是一样的。

剩下总的情况可以枚举一个区间和一个端点,即枚举 \(l1,r1,l2\),将 \(r2,l3,r3\) 存入桶即可。注意对于这个不完整的区间 \([l2,r2]\) 要同时存桶同时算。

点击查看代码
int A, B, C; A = B = C = 0;
cin >> n;
rep(i, 1, n) cin >> a[i], sum[i] = sum[i - 1] + a[i];
rep(i, 1, n) rep(j, i, n) b[qry(i, j) + V] ++;
B = b[0 + V];
rep(i, 1, n) rep(j, i, n) C += (b[-2 * qry(i, j) + V] - (qry(i, j) == 0 ? 1 : 0)) * 3;
rep(k, 1, n) {
	rep(i, 1, n) rep(j, i, n) c[qry(i, j) - sum[k - 1] + V] ++;
	rep(i, 1, n) rep(j, i, n) A += c[-qry(i, j) - sum[k] + V];
}
cout << (A - B - C) / 6;

2025/7/11

JOISC2016 电报

有向图强连通等价于每个点的出入度均为 \(1\),于是贪心的将入度大于 \(1\) 的点的入边删掉,剩下的图一定是一堆小环,考虑怎么把两个环接一起,唯一的方法是“复活”之前删的某一条边,并且断掉一条环边,这样才能让两个环相连,于是预处理非环边的代价最大值,枚举删掉哪条环边最优即可。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for(int i = j; i <= k; ++i)
#define pre(i, j, k) for(int i = j; i >= k; --i)
#define pb push_back
#define PII pair<int, int>
#define fi first
#define se second
#define int long long

using namespace std;
using LL = long long;

const int N = 1e6 + 5;
const int mod = 998244353;
const int inf = 1e9;

#define cadd(a, b) (a = ((a + b) % mod + mod) % mod)
#define cdel(a, b) (a = ((a - b) % mod + mod) % mod)
#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)

int n, m, ret, a[N], c[N], mx[N], vp[N], vis[N];
vector<int> e[N], vec[N];

signed main() {
	FASTIO;
	
	cin >> n;
	rep(i, 1, n) cin >> a[i] >> c[i], vec[a[i]].pb(i), ret += c[i];
	rep(i, 1, n) {
		if(!vis[i]) {
			int x = 0, cnt = 0;
			for(x = i; !vis[x]; x = a[x]) vis[x] = i;
			if(vis[x] == i) {
				for(int y = x; vis[y] != -1; y = a[y]) vis[y] = -1, ++cnt;
			}
			if(cnt == n) cout << 0, exit(0);
		}
	}
	rep(i, 1, n) {
		cmax(mx[a[i]], c[i]);
		if(vis[i] != -1) cmax(vp[a[i]], c[i]);
	} 
	rep(i, 1, n) ret -= mx[i];
	rep(i, 1, n) {
		if(vis[i] == -1) {
			int mn = 1e12;
			for(int x = i; vis[x] == -1; x = a[x]) cmin(mn, mx[x] - vp[x]), vis[x] = 0;
			ret += mn;
		}
	}

	cout << ret;
	return 0;
}

2025/07/21

P2257

莫反,经典结论转化求:

\[\sum\limits_{p\in P}\sum\limits_{i=1}^{\lfloor \frac{n}{p} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{p} \rfloor}[\gcd(i,j)=1] \]

转化一下变成:

\[\sum\limits_{p\in P}\sum\limits_{d=1}^{\min(\frac{n}{p}, \frac{m}{p})} \mu(d)\times \lfloor \frac{n}{pd} \rfloor \times \lfloor \frac{m}{pd} \rfloor \]

换一下元最后变成(\(t=pd\)):

\[\sum\limits_{t=1}^{\min(n, m)}\lfloor \frac{n}{t} \rfloor \lfloor \frac{m}{t} \rfloor \times \sum \limits_{d\in P \And d\mid t} \mu(\frac{t}{d}) \]

\(f(x)=\sum \limits_{d\in P, d\mid x} \mu(\frac{x}{d})\),这东西是可以做前缀和的,那么可以整除分块。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define fv inline void
#define int long long

using LL = long long;
using i16 = int;
using i32 = long long;
using i64 = __int128;
using u32 = unsigned long long;
using namespace std;

const int N = 1e7 + 5;
const int mod = 998244353;

#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)
#define cadd(a, b) (a = ((a + b) % mod + mod) % mod)
#define cdel(a, b) (a = ((a - b) % mod + mod) % mod)



i32 qkpow(i32 a, i32 b) {
	i32 ret = 1;
	for (; b; b >>= 1, a = a * a % mod) if (b & 1) ret = ret * a % mod;
	return ret;
}

i16 vis[N], mu[N];
i32 n, m, k, ret, pr[N], s[N];

vector<int> p[N];

i32 calc(i32 n, i32 m, i32 k) {
	i32 ret = 0; n /= k, m /= k;
	for(i32 l = 1, r; l <= min(n, m); l = r + 1) {
		r = min(n / (n / l), m / (m / l));
		ret += (n / l) * (m / l) * (s[r] - s[l - 1]);
	}
	return ret;
}

fv init(i32 V) {
	mu[1] = 1;
	rep(i, 2, V) {
		if(!vis[i]) pr[++m] = i, mu[i] = -1;
		for(i32 j = 1; j <= m && i * pr[j] <= V; ++j) {
			vis[i * pr[j]] = 1;
			if(i % pr[j] == 0) {
				mu[i * pr[j]] = 0;
				break;
			}
			mu[i * pr[j]] = -mu[i];
		}
	}
	rep(i, 1, m) {
		for(i32 j = pr[i]; j <= V; j += pr[i]) {
			s[j] += mu[j / pr[i]];
		}
	}
	rep(i, 1, V) s[i] += s[i - 1];
}

signed main() {
	FASTIO;
//	freopen("1.in", "r", stdin);
	init(1e7);
	i32 _; cin >> _;
	while(_--) {
		i32 a, b, c, d;
		cin >> a >> b; cout << calc(a, b, 1) << '\n';
	}
	return 0;
}

posted @ 2025-06-24 17:29  Iron_Spade  阅读(11)  评论(0)    收藏  举报