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
莫反,经典结论转化求:
转化一下变成:
换一下元最后变成(\(t=pd\)):
令 \(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;
}

浙公网安备 33010602011771号