2025年05月随便做做
省选破防了,这两个月没怎么写题解。
测试题目选集
2025 四川省省队集训
作为“精英选手”参加,感觉到了队爷阶级的压迫感/lh。
不过感觉大家都好菜,今年不会无集训队吧(爆)。
D1T1 - string
考虑子串周期循环的经典做法,枚举循环长度 \(d\),并把 \(d,2d,3d\cdots\) 这些位置标记成关键位置。此时任意一个循环次数大于 \(1\) 的区间都至少经过了一个形如 \([kd,(k+1)d)\) 的区间,且容易思考出一个极长循环区间的定义,满足 \([l,l+kd),[l+1,l+kd+1),\cdots[r-kd,r+1)\) 均为循环次数为 \(k\) 的周期循环区间。且在 \(d\) 相同的时候,任意两个极长区间满足没有包含关系,且交的长度不超过 \(d\),且每个位置最多被两个极长区间包含。那么可以先求出 SA,然后 \(O(n\ln n)\) 求出这些区间,显然这些区间的个数的数量级比 \(O(n\ln n)\) 小。
第一问此时已经做完(通过 SA 求出的 rk 作为参考,枚举极长区间即可)。考虑之后的每个询问,与极长区间求交之后计算贡献即可。但是这样比较慢且容易爆。对于任意极长区间 \([l,r]\),将 \([l+d,r],[l+2d,r]\cdots\) 和 \([l,r-d],[l,r-2d]\cdots\) 加入,显然总和仍然是 \(O(n\ln n)\) 的,而现在我们对于一个询问只用考虑它包含的区间以及包含它的区间的贡献。最终转化成单点修改,前/后缀求min/max,可以使用树状数组快速维护,估计时间复杂度为 \(O(n\ln n\log_2 n)\)。
这时我有一个小巧思,就是对于 \([l,r]\) 相同的只保留 \(d\) 最小的那个,这样感觉就没有区间的意义能够完全另一个区间了。此时猜测区间个数为 \(O(n)\)。事实证明正确,这个似乎是 runs 的相关结论。
感觉算是签,但是我花了 2.5h /kel。但是为什么没人过啊,大家难道不会 SA 吗。
Code
// Go in my style.
// Not afraid to dark.
#include<bits/stdc++.h>
using namespace std;
#define inline __inline__ __attribute__((always_inline))
namespace io {
int pos;
inline int read(int &p = pos) {
static int v; static char c;
p = 0, v = 1, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = -1;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v == 1 ? p : -p;
}
inline void write(int x) {
if (x < 0) putchar('-'), x = -x;
static int sta[65], top; top = 0;
do {
sta[++top] = x % 10;
x /= 10;
} while (x);
while (top) putchar(sta[top--] + 48);
}
}
#define log2_(x) (31 ^ __builtin_clz(x))
const int N = 2e6;
int n, m, ans[N + 5];
char c[N + 5];
struct que {
int l, r, i;
que(int _l_ = 0, int _r_ = 0, int _i_ = 0) { l = _l_, r = _r_, i = _i_; }
} q[N + 5];
struct SA {
char s[N + 5];
int ton[N + 5], rk[N + 5], sa[N + 5], pos[N + 5];
int ht[N + 5], sth[25][N + 5];
inline int rep(const int x) { return x > n ? -1 : pos[x]; }
inline void get_sa() {
int V = max(256, n);
for (int i = 0; i <= V; ++i) ton[i] = 0;
for (int i = 1; i <= n; ++i) ++ton[s[i]];
for (int i = 1; i <= V; ++i) ton[i] += ton[i - 1];
for (int i = 1; i <= n; ++i) rk[i] = ton[s[i]];
for (int i = n; i >= 1; --i) sa[ton[s[i]]--] = i;
// auto rep = [](const int x) { return x > n ? -1 : pos[x]; };
for (int l = 1; l <= n; l <<= 1) {
int cnt = 0;
for (int i = n - l + 1; i <= n; ++i) pos[++cnt] = i;
for (int i = 0; i <= V; ++i) ton[i] = 0;
for (int i = 1; i <= n; ++i) {
if (sa[i] > l)
pos[++cnt] = sa[i] - l;
++ton[rk[i]];
}
for (int i = 1; i <= V; ++i) ton[i] += ton[i - 1];
for (int i = n; i >= 1; --i) sa[ton[rk[pos[i]]]--] = pos[i];
for (int i = 1; i <= n; ++i) pos[i] = rk[i];
rk[sa[cnt = 1]] = 1;
for (int i = 2; i <= n; ++i)
if (rep(sa[i]) == rep(sa[i - 1]) && rep(sa[i] + l) == rep(sa[i - 1] + l))
rk[sa[i]] = cnt;
else
rk[sa[i]] = ++cnt;
}
for (int i = 1; i <= n; ++i) {
ht[rk[i]] = max(ht[rk[i - 1]] - 1, 0);
int &l = ht[rk[i]];
while (i + l <= n && sa[rk[i] - 1] + l <= n && s[i + l] == s[sa[rk[i] - 1] + l]) ++l;
}
for (int i = 1; i <= n; ++i)
sth[0][i] = ht[i];
for (int i = 1; i <= 20; ++i)
for (int j = 1; j <= n; ++j) {
sth[i][j] = sth[i - 1][j];
if (j + (1 << (i - 1)) <= n)
sth[i][j] = min(sth[i][j], sth[i - 1][j + (1 << (i - 1))]);
}
}
inline int lcp(int x, int y) {
if (x == y) return n - x + 1;
x = rk[x], y = rk[y];
if (x > y) swap(x, y);
++x;
static int p; p = log2_(y - x + 1);
return min(sth[p][x], sth[p][y - (1 << p) + 1]);
}
} pr, sf;
int strk[25][N + 5];
inline int minprrk(int i, int j) { return pr.rk[i] < pr.rk[j] ? i : j; }
inline int fdmrk(int l, int r) {
static int p; p = log2_(r - l + 1);
return minprrk(strk[p][l], strk[p][r - (1 << p) + 1]);
}
inline int prlcp(int i, int j) { return pr.lcp(i, j); }
inline int sflcp(int i, int j) { return sf.lcp(n - i + 1, n - j + 1); }
vector<que> seq, tmp;
vector<que> tl[N + 5], tlq[N + 5], tr[N + 5], trq[N + 5];
inline void inse(int l, int r, int i) {
if ((r - l + 1) / i > 1) seq.push_back(que(l, r, i));
}
namespace BIT {
inline int lowbit(const int x) { return x & -x; }
int ar[N + 5];
inline void Upd(int p, int x) {
while (p <= n && ar[p] < x) ar[p] = x, p += lowbit(p);
}
inline int Que(int p) {
static int res; res = 0;
while (p > 0) res = max(res, ar[p]), p ^= lowbit(p);
return res;
}
inline void Clear() { for (int i = 1; i <= n; ++i) ar[i] = 0; }
}
namespace _BIT {
inline int lowbit(const int x) { return x & -x; }
int ar[N + 5];
inline void Upd(int p, int x) {
while (p <= n && ar[p] > x) ar[p] = x, p += lowbit(p);
}
inline int Que(int p) {
static int res; res = n;
while (p > 0) res = min(res, ar[p]), p ^= lowbit(p);
return res;
}
inline void Clear() { for (int i = 1; i <= n; ++i) ar[i] = n; }
}
signed main() {
// freopen("string.in", "r", stdin);
// freopen("string.out", "w", stdout);
scanf("%s", c + 1); n = strlen(c + 1); io::read(m);
for (int i = 0; i <= n + 1; ++i) pr.s[i] = c[i], sf.s[i] = c[n - i + 1];
pr.get_sa(), sf.get_sa();
for (int i = 1; i <= n; ++i) strk[0][i] = i;
for (int i = 1; i <= 20; ++i)
for (int j = 1; j <= n; ++j) {
strk[i][j] = strk[i - 1][j];
if (j + (1 << (i - 1)) <= n)
strk[i][j] = minprrk(strk[i][j], strk[i - 1][j + (1 << (i - 1))]);
}
for (int i = 1; i <= m; ++i) {
io::read(q[i].l), io::read(q[i].r); q[i].i = i;
tlq[q[i].l].emplace_back(q[i]);
trq[q[i].r].emplace_back(q[i]);
}
for (int len = 1; len <= n; ++len) {
{
int l = 1, r = len;
for (int i = 1 + len; i <= n; i += len) {
r = i + min(prlcp(1, i), len) - 1;
if (r != i + len - 1) break;
}
inse(l, r, len);
}
for (int i = 1, j = len; ; i += len, j += len) {
int ni = i + len, nj = j + len;
if (nj > n) break;
int l = j - min(sflcp(nj, j), len) + 1, r = nj;
if (l == i) continue;
for (int k = ni + len; k <= n; k += len) {
r = k + min(prlcp(k - len, k), len) - 1;
if (r != k + len - 1) break;
}
inse(l, r, len);
}
}
{
int ansl = pr.sa[1], ansr = ansl, ansv = 1;
for (que x : seq) {
int xv = (x.r - x.l + 1) / x.i, mnl = fdmrk(x.l, x.r - xv * x.i + 1);
if (ansv < xv || (ansv == xv && pr.rk[ansl] > pr.rk[mnl]))
ansl = mnl, ansr = mnl + xv * x.i - 1, ansv = xv;
}
for (int i = ansl; i <= ansr; ++i) putchar(c[i]);
puts("");
}
sort(seq.begin(), seq.end(), [](const que &x, const que &y) {
if (x.l == y.l) return x.r == y.r ? x.i < y.i : x.r < y.r;
return x.l < y.l;
});
for (int i = 0; i < (int)seq.size(); ++i)
if (i == 0 || seq[i].l != seq[i - 1].l || seq[i].r != seq[i - 1].r)
tmp.emplace_back(seq[i]);
swap(tmp, seq); tmp.clear();
for (que x : seq) tl[x.l].emplace_back(x), tr[x.r].emplace_back(x);
for (int i = 1; i <= m; ++i) ans[i] = 1;
BIT::Clear();
for (int i = n; i >= 1; --i) {
for (que x : tl[i])
for (int r = x.l + x.i - 1, cnt = 1; r <= x.r; r += x.i, ++cnt)
BIT::Upd(r, cnt);
for (que Q : tlq[i])
ans[Q.i] = max(ans[Q.i], BIT::Que(Q.r));
}
BIT::Clear();
for (int i = 1; i <= n; ++i) {
for (que x : tr[i])
for (int l = x.r - x.i + 1, cnt = 1; l >= x.l; l -= x.i, ++cnt)
BIT::Upd(n - l + 1, cnt);
for (que Q : trq[i])
ans[Q.i] = max(ans[Q.i], BIT::Que(n - Q.l + 1));
}
_BIT::Clear();
for (int i = n; i >= 1; --i) {
for (que x : tr[i])
_BIT::Upd(x.l, x.i);
for (que Q : trq[i])
ans[Q.i] = max(ans[Q.i], (Q.r - Q.l + 1) / _BIT::Que(Q.l));
}
_BIT::Clear();
for (int i = 1; i <= n; ++i) {
for (que x : tl[i])
_BIT::Upd(n - x.r + 1, x.i);
for (que Q : tlq[i])
ans[Q.i] = max(ans[Q.i], (Q.r - Q.l + 1) / _BIT::Que(n - Q.r + 1));
}
for (int i = 1; i <= m; ++i) io::write(ans[i]), putchar('\n');
return 0;
}
D1T2 - matrix
感觉也是签,3min 瞪出 flow,以为看错题就上了个厕所冷静一下,然后发现好像是对的,然后花了 20min 写完直接就过 pretest 了。
考虑把 \((i,j)\) 当成第 \(i\) 条横线和第 \(j\) 条竖线的交点,将有限制关系的点画出来发现偶数格子上均有形如叉号的两条边,那么考虑把这个叉号当成点,原本的点当成两个叉号之间的连边,然后就是经典二分图带权匹配,费用流即可。
怎么又没人过这个题
Code
#include<bits/stdc++.h>
using namespace std;
#define inline __inline__ __attribute__((always_inline))
#define int long long
namespace io {
int pos;
inline int read(int &p = pos) {
static int v; static char c;
p = 0, v = 1, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = -1;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v == 1 ? p : -p;
}
inline void write(int x) {
if (x < 0) putchar('-'), x = -x;
static int sta[65], top; top = 0;
do {
sta[++top] = x % 10;
x /= 10;
} while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int NM = 1000;
int n, m, a[NM + 5], iep[NM + 5];
namespace FLOW {
const int P = NM * 20 + 5, E = NM * 20 + 5, inf = 0x3f3f3f3f3f3f3f3f;
int pn, s, t;
int firs[P], nex[E], to[E], w[E], c[E], tot = 1;
inline int Add(int u, int v, int l, int x) {
nex[++tot] = firs[u], firs[u] = tot, to[tot] = v, w[tot] = l, c[tot] = x;
nex[++tot] = firs[v], firs[v] = tot, to[tot] = u, w[tot] = 0, c[tot] = -x;
return tot;
}
int dis[P], cur[P], pre[P];
bool inq[P];
inline bool SPFA() {
for (int i = 1; i <= pn; ++i)
dis[i] = -inf, cur[i] = pre[i] = 0, inq[i] = false;
queue<int> Q;
dis[s] = 0, cur[s] = inf, pre[s] = -1;
Q.emplace(s);
while (!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = false;
for (int e = firs[u], v; e; e = nex[e]) {
v = to[e];
if (w[e] && dis[v] < dis[u] + c[e]) {
dis[v] = dis[u] + c[e];
cur[v] = min(w[e], cur[u]);
pre[v] = e;
if (!inq[v]) {
Q.emplace(v);
inq[v] = true;
}
}
}
}
return dis[t] > 0 && cur[t];
}
inline int Maxcost() {
static int cos; cos = 0;
while (SPFA()) {
cos += dis[t] * cur[t];
// printf("%lld %lld %lld\n", dis[t], cur[t], to[pre[t] ^ 1]);
int u = t;
while (u != s) {
w[pre[u]] -= cur[t];
w[pre[u] ^ 1] += cur[t];
u = to[pre[u] ^ 1];
// printf("%lld\n", u);
}
}
return cos;
}
}
inline int id(int i, int j) { return i * (m + 1) + j + 1; }
inline int aid(int i, int j) { return (i - 1) * m + j; }
signed main() {
// freopen("matrix.in", "r", stdin);
// freopen("matrix.out", "w", stdout);
io::read(n), io::read(m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
io::read(a[aid(i, j)]);
{
using namespace FLOW;
pn = id(n, m);
s = ++pn, t = ++pn;
for (int i = 1, j, u, v, I; i <= n; ++i)
for (j = 1; j <= m; ++j)
if ((i ^ j) & 1) {
u = id(i - 1, j - 1), v = id(i, j), I = aid(i, j);
if (i & 1) iep[I] = Add(u, v, 1, a[I]);
else iep[I] = Add(v, u, 1, a[I]);
} else {
u = id(i - 1, j), v = id(i, j - 1), I = aid(i, j);
if (i & 1) iep[I] = Add(u, v, 1, a[I]);
else iep[I] = Add(v, u, 1, a[I]);
}
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= m; ++j)
if (i & 1) Add(id(i, j), t, 1, 0);
else Add(s, id(i, j), 1, 0);
// puts("in");
io::write(Maxcost()), putchar('\n');
// puts("in");
for (int i = 1, j; i <= n; ++i, putchar('\n'))
for (j = 1; j <= m; ++j, putchar(' '))
if (w[iep[aid(i, j)]]) putchar('1');
else putchar('0');
}
return 0;
}
D1T3 - popsicle
不太会,只打了 28 分。事实证明有一种巧思做法较慢但是既好写又好想,拜谢 lh 大神。
巧思做法是枚举与 2,3,5,7,9 的余数。
不过正解似乎也是巧思。考虑如果两两互质,那么出现是均匀的所以直接相乘就行了。考虑枚举一个数 \(Q\),然后只用对每个 \(t < Q\) 求出概率然后就行了,考虑模 \(Q\) 意义下周期会从 \(l_i\) 变为 \(\frac{l_i}{\gcd(l_i, Q)}\)。于是希望让这些值互质,得出一个大概 \(10^4\) 左右的常数 \(Q\),时间复杂度 \(O(TQV^2)\)。
但是有多测是过不了的。不过考虑其实只需要让这些值要么有倍数关系要么互质,就可以了。所以得到了 \(Q'=2520\) 时满足条件,时间复杂度 \(O(TQ'V^2)\)。
Code
// Go in my style.
// Not afraid to dark.
#include <bits/stdc++.h>
using namespace std;
clock_t sttime;
#define STCLOCK sttime = clock();
#define TIMENOW fprintf(stderr, "\nNOW TIME COSSEMED: %0.4lf\n", 1.0 * (clock() - sttime) / CLOCKS_PER_SEC);
#define inline __inline__ __attribute__ ((always_inline))
#define int long long
namespace io {
int pos;
inline int read(int &p = pos) {
static bool v; static char c;
p = 0, v = false, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = true;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v ? - p : p;
}
inline void write(int x) {
static int sta[65], top; top = 0;
if (x < 0) putchar('-'), x = - x;
do sta[++top] = x % 10, x /= 10;
while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int M = 2520, V = 100, mod = 998244353;
int n, s[V + 5][V + 5], sp[V + 5][V + 5];
bool exi[V + 5];
inline int power(int a, int p) {
static int res; res = 1;
while (p > 0) {
if (p & 1) res = res * a % mod;
a = a * a % mod, p >>= 1;
}
return res;
}
inline void ARKNIGHTS() {
for (int i = 1; i <= V; ++i)
for (int j = 0; j < V; ++j)
s[i][j] = 1;
io::read(n);
for (int i = 1, l; i <= n; ++i) {
io::read(l);
for (int j = 0; j < l; ++j)
s[l][j] = s[l][j] * (mod + 1 - io::read()) % mod;
}
int sum = 0;
for (int T = 0; T < M; ++T) {
for (int i = 1; i <= V; ++i) {
exi[i] = false;
for (int j = 0; j < i; ++j)
sp[i][j] = 1;
}
for (int i = 1, u, v; i <= V; ++i) {
u = v = T % i;
vector<int> tmp;
do {
tmp.emplace_back(s[i][u]);
u = (u + M) % i;
} while (u != v);
exi[(int)tmp.size()] = true;
for (int j = 0, sz = (int)tmp.size(); j < sz; ++j)
sp[sz][j] = sp[sz][j] * tmp[j] % mod;
}
for (int i = 1; i <= V; ++i)
if (exi[i])
for (int j = i * 2; j <= V; j += i)
if (exi[j]) {
exi[i] = false;
for (int k = 0; k < j; ++k)
sp[j][k] = sp[j][k] * sp[i][k % i] % mod;
break;
}
int fm = 1, fz = 1;
for (int i = 1; i <= V; ++i)
if (exi[i]) {
fm = fm * i % mod;
int sm = 0;
for (int j = 0; j < i; ++j) sm += sp[i][j];
fz = sm % mod * fz % mod;
}
sum = (sum + mod + 1 - fz * power(fm, mod - 2) % mod) % mod;
}
sum = sum * power(M, mod - 2) % mod;
io::write(sum), putchar('\n');
}
signed main() {
STCLOCK
// freopen("popsicle.in", "r", stdin);
// freopen("popsicle.out", "w", stdout);
for (int T = io::read(); T--; ARKNIGHTS()) ;
TIMENOW
return 0;
}
D2T1 - palindrome
你们签到题都这么爱放串吗
第一眼感觉条件很满足 manacher 算法,事实也是如此,考虑 manacher 的关键操作“对称” 是否可以成功,发现本质是映射两遍,显然是正确的。那么可以对每个中点求出左右延伸的最长长度。
那么难点在于如何求出每个中点的贡献。已知区间 \([l,r]\) 的贡献为 \(m^{m-app(l,r)}\),其中 \(app(l,r)\) 表示区间出现的元素个数。那么发现对称时在某些情况下可以直接将贡献复制过去。而不能的情况是当前点复制长度后已超出原本的 \(\max r\),而需要重新计算贡献时,复制过来的贡献需要减去一部分。考虑暴力减去,发现可以过。不过稍加分析可以发现这是正确的。考虑 \(\max r\) 对应的 \(l\) 的一种移动方式的移动步数,每次先从 \(l\) 移动到 \(\max r - 2len-1\),然后再随着新的 \(\max r\) 的增长而变小。容易发现每次向右移动当作消耗势能,向左移动当作积蓄势能,那么势能初始为 \(O(n)\),因为向左走是随着 \(\max r\) 向右时一起的,所以积蓄的势能也是 \(O(n)\) 级别的,因此 \(l\) 的上述移动的步数是 \(O(n)\) 的,且不超过 \(2n\)。
画出图像容易发现每次暴力减去的区间都包含在 \(l\) 的上述移动的路径上且每次的路径互不相交,因此减去的次数也是 \(O(n)\) 的。
于是可以 \(O(n)\) 解决这个问题。
不要使用 vector 记录位置,否则会 TLE,只有 80 分。
Code
// Go in my style.
// Not afraid to dark.
#include <bits/stdc++.h>
using namespace std;
#define inline __inline__ __attribute__ ((always_inline))
namespace io {
int pos;
inline int read(int &p = pos) {
static bool v; static char c;
p = 0, v = false, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = true;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v ? - p : p;
}
inline void write(int x) {
static int sta[65], top; top = 0;
if (x < 0) putchar('-'), x = - x;
do sta[++top] = x % 10, x /= 10;
while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int N = 5e6, mod = 998244353;
int n, m, a[N + 5], len[N + 5];
int pw[N + 5], ans;
int pre[N + 5], suf[N + 5];
vector<int> pos[N + 5];
inline bool Check(int l, int r) {
if (l == r) return true;
bool res = true;
int i;
i = suf[l];
if (i < r) res = res && a[r] == a[l + r - i];
i = pre[r];
if (i > l) res = res && a[l] == a[l + r - i];
return res;
}
inline int Appe(int l, int r) {
if (l == r) return 1;
int rr = 0, i;
i = suf[l];
if (i >= r) ++rr;
i = pre[r];
if (i <= l) ++rr;
if (a[l] == a[r]) rr = min(rr, 1);
return rr;
}
struct info {
int ap, nv;
info() { ap = m, nv = 0; }
inline void del(int x) {
nv = (nv - pw[ap] + mod) % mod;
ap += x;
}
inline void ins(int x) { nv = (nv + pw[ap -= x]) % mod; }
} bas[N + 5];
int ton[N + 5];
signed main() {
// freopen("palindrome.in", "r", stdin);
// freopen("palindrome.out", "w", stdout);
io::read(n), io::read(m);
for (int i = 1; i <= m; ++i) ton[i] = -1;
for (int i = 1; i <= n; ++i) {
io::read(a[i]);
pre[i] = ton[a[i]];
ton[a[i]] = i;
}
for (int i = 1; i <= m; ++i) ton[i] = n + 1;
for (int i = n; i >= 1; --i) {
suf[i] = ton[a[i]];
ton[a[i]] = i;
}
pw[0] = 1;
for (int i = 1; i <= m; ++i) pw[i] = 1ll * pw[i - 1] * m % mod;
// odd len
for (int i = 1, md = 0, r = 0; i <= n; ++i) {
if (r < i) {
bas[i] = info();
for (r = i; r <= n && 2 * i - r > 0 && Check(2 * i - r, r); ++r) bas[i].ins(Appe(2 * i - r, r)); --r;
md = i; len[i] = r - i;
} else {
int o = 2 * md - i;
len[i] = len[o], bas[i] = bas[o];
if (i + len[i] >= r) {
while (i + len[i] > r) {
bas[i].del(Appe(o - len[i], o + len[i]));
--len[i];
}
for (++r; r <= n && 2 * i - r > 0 && Check(2 * i - r, r); ++r) bas[i].ins(Appe(2 * i - r, r)); --r;
md = i; len[i] = r - i;
}
}
ans = (ans + bas[i].nv) % mod;
}
// even len
for (int i = 1, md = 0, r = 0; i <= n; ++i) {
if (r < i) {
bas[i] = info();
for (r = i; r <= n && 2 * i - 1 - r > 0 && Check(2 * i - 1 - r, r); ++r) bas[i].ins(Appe(2 * i - 1 - r, r)); --r;
md = i; len[i] = r - i;
} else {
int o = 2 * md - i;
len[i] = len[o], bas[i] = bas[o];
if (i + len[i] >= r) {
while (i + len[i] > r) {
bas[i].del(Appe(o - len[i] - 1, o + len[i]));
--len[i];
}
for (++r; r <= n && 2 * i - 1 - r > 0 && Check(2 * i - 1 - r, r); ++r) bas[i].ins(Appe(2 * i - 1 - r, r)); --r;
md = i; len[i] = r - i;
}
}
ans = (ans + bas[i].nv) % mod;
}
io::write(ans), putchar('\n');
return 0;
}
D2T2 - fuse
考虑从根往下枚举 \(u\) ,容易发现 \(u\) 的相关集合是以 \(u\) 为根的一个连通子树,且因为每次 \(u\) 向下走而被加入的点一定是此时相关集合的叶子。那么就可以树剖+线段树做到 \(O(n\log_2^2 n)\),因为常数极小所以可以通过本题。
正解 \(O(n\log_2n)\) 考虑 \(O(n)\) 建立虚树后进行长链之类的分析。
Code
#include<bits/stdc++.h>
using namespace std;
#define inline __inline__ __attribute__((always_inline))
#define ll long long
namespace io {
int pos;
inline int read(int &p = pos) {
static int v; static char c;
p = 0, v = 1, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = -1;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v == 1 ? p : -p;
}
inline void write(int x) {
if (x < 0) putchar('-'), x = -x;
static int sta[65], top; top = 0;
do {
sta[++top] = x % 10;
x /= 10;
} while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int N = 1e6, mod = 994007158;
int n, fa[N + 5], Fib[N + 5], ans;
inline int &MOD(int &x) { return x >= mod ? x -= mod : x; }
int idx, dfn[N + 5], rep[N + 5], sz[N + 5], son[N + 5], top[N + 5], deg[N + 5];
int lf[N + 5], dep[N + 5];
vector<int> to[N + 5], ins[N + 5];
void init1(int u) {
sz[u] = 1;
lf[u] = n;
dep[u] = dep[fa[u]] + 1;
for (int v : to[u]) {
init1(v);
sz[u] += sz[v];
lf[u] = min(lf[u], lf[v] + 1);
if (!son[u] || sz[son[u]] < sz[v]) son[u] = v;
}
if (lf[u] == n) lf[u] = 0;
}
void init2(int u, bool flg = true) {
dfn[u] = ++idx;
rep[idx] = u;
if (flg) top[dfn[u]] = dfn[u];
else top[dfn[u]] = top[dfn[fa[u]]];
if (son[u]) {
for (int v : to[u]) lf[v] = min(lf[v], lf[u]);
init2(son[u], false);
for (int v : to[u]) if (v != son[u])
init2(v);
}
}
void init3(int u) {
if (dep[u] - 1 <= lf[u]) ins[1].emplace_back(u);
else {
int v = dfn[u], rm = lf[u];
while (rm > 0) {
if (v - top[v] < rm) rm -= v - top[v] + 1, v = dfn[fa[rep[top[v]]]];
else {
v -= rm;
rm = 0;
}
}
ins[rep[v]].emplace_back(u);
}
for (int v : to[u]) init3(v);
}
namespace sgt {
#define lson(p) ((p) << 1)
#define rson(p) ((p) << 1 | 1)
const int P = N << 2;
int lt[P], rt[P], sum[P], tag[P];
#define put_tag(p, x) sum[p] = ((ll)sum[p]) * x % mod, tag[p] = ((ll)tag[p]) * x % mod, void()
#define push_up(p) MOD(sum[p] = sum[lson(p)] + sum[rson(p)]), void()
inline void push_down(int p) {
if (tag[p] != 1) {
put_tag(lson(p), tag[p]), put_tag(rson(p), tag[p]);
tag[p] = 1;
}
}
void Build(int p, int l, int r) {
lt[p] = l, rt[p] = r;
sum[p] = 0, tag[p] = 1;
if (l == r) return ;
int mid = (lt[p] + rt[p]) >> 1;
Build(lson(p), l, mid), Build(rson(p), mid + 1, r);
}
void Acti(int p, int x) {
if (lt[p] == rt[p]) return sum[p] = 1, void();
push_down(p);
int mid = (lt[p] + rt[p]) >> 1;
if (x <= mid) Acti(lson(p), x);
else Acti(rson(p), x);
push_up(p);
}
void Upd(int p, int l, int r, int x) {
if (l <= lt[p] && rt[p] <= r) return put_tag(p, x);
push_down(p);
int mid = (lt[p] + rt[p]) >> 1;
if (l <= mid) Upd(lson(p), l, r, x);
if (r > mid) Upd(rson(p), l, r, x);
push_up(p);
}
int Que(int p, int l, int r) {
if (l <= lt[p] && rt[p] <= r) return sum[p];
push_down(p);
int mid = (lt[p] + rt[p]) >> 1, res = 0;
if (l <= mid) res += Que(lson(p), l, r);
if (r > mid) res += Que(rson(p), l, r);
return MOD(res);
}
#undef lson
#undef rson
#undef put_tag
#undef push_up
}
void sol(int u) {
for (int x : ins[u]) {
sgt::Acti(1, dfn[x]);
int w = Fib[deg[x]], y = dfn[x];
while (y >= dfn[u]) {
sgt::Upd(1, top[y], y, w);
y = rep[top[y]];
y = dfn[fa[y]];
}
}
int f = sgt::Que(1, dfn[u], dfn[u] + sz[u] - 1);
ans ^= f;
if (son[u]) {
sol(son[u]);
for (int v : to[u]) if (v != son[u]) sol(v);
}
}
signed main() {
// freopen("fuse.in", "r", stdin);
// freopen("fuse.out", "w", stdout);
io::read(n);
Fib[1] = Fib[2] = 1;
for (int i = 3; i <= n; ++i) MOD(Fib[i] = Fib[i - 1] + Fib[i - 2]);
for (int i = 2; i <= n; ++i) to[io::read(fa[i])].emplace_back(i), ++deg[i], ++deg[fa[i]];
init1(1);
init2(1);
init3(1);
sgt::Build(1, 1, n);
sol(1);
io::write(ans), putchar('\n');
return 0;
}
D2T3
Code
D3T1 - inferiority
考虑显然可以拆出不同的位数来分块预处理后求和,时间复杂度为 \(O(n\sqrt n \log_2 n)\)。然后考虑空间复杂度,于是离线处理,时间复杂度仍然是 \(O(n\sqrt n \log_2 n)\),空间复杂度 \(O(n+\sqrt n \log_2 n)\)。
Code
#include<bits/stdc++.h>
using namespace std;
#define inline __inline__ __attribute__((always_inline))
namespace io {
int pos;
inline int read(int &p = pos) {
static int v; static char c;
p = 0, v = 1, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = -1;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v == 1 ? p : -p;
}
inline void write(long long x) {
if (x < 0) putchar('-'), x = -x;
static int sta[65], top; top = 0;
do {
sta[++top] = x % 10;
x /= 10;
} while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int N = 1e5, SQ = 350, LG = 17;
int n, m, a[N + 5];
pair<int, int> b[N + 5];
struct node { int l, r, i; } que[N + 5];
int sq, len, block[N + 5], bl[SQ + 5], br[SQ + 5], cnt[SQ + 5][LG][2];
long long A[SQ + 5][SQ + 5], ans[N + 5];
// A[I<SQ>][J<SQ>] block-to-block
vector<int> qt[N + 5];
inline long long Docon(int la = n + 1, int ra = n, int lb = n + 1, int rb = n) {
vector<int> tmp;
for (int i = la; i <= ra; ++i) tmp.emplace_back(a[i]);
for (int i = lb; i <= rb; ++i) tmp.emplace_back(a[i]);
sort(tmp.begin(), tmp.end());
static int tcnt[LG][2];
for (int i = 0; i < LG; ++i) for (int q = 0; q < 2; ++q) tcnt[i][q] = 0;
long long ret = 0;
for (int i = 0, j = 0; i < (int)tmp.size(); ++i) {
while (tmp[j] * 2 < tmp[i]) {
for (int o = 0; o < LG; ++o)
++tcnt[o][tmp[j] >> o & 1];
++j;
}
for (int o = 0; o < LG; ++o)
ret += tcnt[o][(tmp[i] >> o & 1) ^ 1] << o;
}
return ret;
}
inline void Sol() {
sq = len = sqrt(n);
while (sq * len < n) ++len;
for (int i = 1; i <= sq; ++i) {
bl[i] = br[i - 1] + 1, br[i] = min(n, bl[i] + len - 1);
for (int j = bl[i]; j <= br[i]; ++j)
block[j] = i;
}
for (int i = 1; i <= n; ++i) b[i] = {a[i], i};
sort(b + 1, b + n + 1);
for (int i = 1, j = 1; i <= n; ++i) {
while (b[j].first * 2 < b[i].first) {
int id = b[j].second;
for (int o = 0; o < LG; ++o)
++cnt[block[id]][o][a[id] >> o & 1];
++j;
}
int id = b[i].second;
for (int bi = 1; bi <= sq; ++bi)
for (int o = 0; o < LG; ++o)
A[block[id]][bi] += cnt[bi][o][(a[id] >> o & 1) ^ 1] << o;
}
for (int i = 1; i <= sq; ++i)
for (int j = 1; j <= sq; ++j)
A[i][j] += A[i][j - 1];
// for (int i = 1, l, r; i <= m; ++i) {
// l = que[i].l, r = que[i].r;
// ans[i] = Docon(l, r);
// }
// return ;
for (int i = 1, l, r; i <= m; ++i) {
l = que[i].l, r = que[i].r;
for (int j = block[l] + 1; j < block[r]; ++j)
ans[i] += A[j][block[r] - 1] - A[j][block[l]];
if (block[l] + 1 >= block[r])
ans[i] += Docon(l, r);
else {
ans[i] += Docon(l, br[block[l]], bl[block[r]], r);
qt[block[l]].emplace_back(i), qt[block[r]].emplace_back(i);
}
}
for (int B = 1; B <= sq; ++B) {
// low
for (int bi = 1; bi <= sq; ++bi)
for (int o = 0; o < LG; ++o)
cnt[bi][o][0] = cnt[bi][o][1] = 0;
for (int i = 0; i <= len; ++i)
for (int j = 0; j <= sq; ++j)
A[i][j] = 0;
for (int i = 1, j = 1; i <= n; ++i) {
while (b[j].first * 2 < b[i].first) {
int id = b[j].second;
for (int o = 0; o < LG; ++o)
++cnt[block[id]][o][a[id] >> o & 1];
++j;
}
int id = b[i].second;
if (bl[B] <= id && id <= br[B])
for (int bi = 1; bi <= sq; ++bi)
for (int o = 0; o < LG; ++o)
A[id - bl[B]][bi] += cnt[bi][o][(a[id] >> o & 1) ^ 1] << o;
}
for (int i = bl[B]; i <= br[B]; ++i)
for (int id = i - bl[B], j = 1; j <= sq; ++j)
A[id][j] += A[id][j - 1];
for (int q : qt[B]) {
int l = que[q].l, r = que[q].r;
for (int i = max(bl[B], l); i <= min(br[B], r); ++i)
ans[q] += A[i - bl[B]][block[r] - 1] - A[i - bl[B]][block[l]];
}
// gre
for (int bi = 1; bi <= sq; ++bi)
for (int o = 0; o < LG; ++o)
cnt[bi][o][0] = cnt[bi][o][1] = 0;
for (int i = 0; i <= len; ++i)
for (int j = 0; j <= sq; ++j)
A[i][j] = 0;
for (int i = n, j = n; i >= 1; --i) {
while (b[j].first > b[i].first * 2) {
int id = b[j].second;
for (int o = 0; o < LG; ++o)
++cnt[block[id]][o][a[id] >> o & 1];
--j;
}
int id = b[i].second;
if (bl[B] <= id && id <= br[B])
for (int bi = 1; bi <= sq; ++bi)
for (int o = 0; o < LG; ++o)
A[id - bl[B]][bi] += cnt[bi][o][(a[id] >> o & 1) ^ 1] << o;
}
for (int i = bl[B]; i <= br[B]; ++i)
for (int id = i - bl[B], j = 1; j <= sq; ++j)
A[id][j] += A[id][j - 1];
for (int q : qt[B]) {
int l = que[q].l, r = que[q].r;
for (int i = max(bl[B], l); i <= min(br[B], r); ++i)
ans[q] += A[i - bl[B]][block[r] - 1] - A[i - bl[B]][block[l]];
}
}
}
signed main() {
// freopen("inferiority.in", "r", stdin);
// freopen("inferiority.out", "w", stdout);
io::read(n), io::read(m);
for (int i = 1; i <= n; ++i) io::read(a[i]);
for (int i = 1; i <= m; ++i) io::read(que[i].l), io::read(que[i].r), que[i].i = i;
Sol();
for (int i = 1; i <= m; ++i) io::write(ans[i]), putchar('\n');
return 0;
}
D3T2 - dsa
考虑经过一系列容斥,转化成求划分出 \(k\) 个集合,求权值之积的和,其中一个集合 \(S\) 的权值是 \(\prod_{i=1}^{|S|} (\sum_{u\in S} a_u + i)\)。这个权值的组合意义就是每个点 \(i\) 向 \(j\) 连 \(a_j+[j\le i]\) 条边,那么所有集合只向集合内的点连一条边边的话,方案数就是上述权值。
那么将构成一个基环树森林。如果钦定 \(k\) 个环求出了权值,可以容斥求出恰好 \(k\) 个环,然后再乘第二类斯特林数划分出 \(k\) 个集合的权值。
那么问题是求出钦定 \(k\) 个环的权值。如果是环外面的点 \(j\),那么权值是 \(j+\sum_{i=1}^n a_i\)。否则在环上的点的权值应该是 \(a_j + [j\le pre(j)]\)。考虑我们可以求出若干条编号递增的链然后乘第一类斯特林数,从而将 \(a_j\) 和 \([j\le nex(j)]\) 分离。但是因为最后要知道有多少链,所以要进行一点变化。将环上的点的权值写成 \(a_j+1-[j>pre(j)]\),那么因为编号递增的链本身可能会被拆成若干条链,所以可以拆成 \(a_j+1\) 和 \(-[j>pre(j)]\) 两部分,然后就可以 dp 了:
然后就行了。注意因为题目要求了方案要本质不同,所以最后还要除掉每个数重复次数的阶乘。
Code
// Go in my style.
// Not afraid to dark.
#include <bits/stdc++.h>
using namespace std;
clock_t sttime;
#define STCLOCK sttime = clock();
#define TIMENOW fprintf(stderr, "\nNOW TIME COSSEMED: %0.4lf\n", 1.0 * (clock() - sttime) / CLOCKS_PER_SEC);
#define inline __inline__ __attribute__ ((always_inline))
#define int long long
namespace io {
int pos;
inline int read(int &p = pos) {
static bool v; static char c;
p = 0, v = false, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = true;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v ? -p : p;
}
inline void write(int x) {
static int sta[65], top; top = 0;
if (x < 0) putchar('-'), x = - x;
do sta[++top] = x % 10, x /= 10;
while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int N = 5e3, mod = 998244353;
int n, a[N + 5], s;
int f[N + 5][N + 5], coef = 1;
int S1[N + 5][N + 5], S2[N + 5][N + 5];
int H[N + 5], G[N + 5], F[N + 5];
int ton[N + 5], inv[N + 5], jc[N + 5], inj[N + 5];
inline int C(int a, int b) { return jc[a] * inj[b] % mod * inj[a - b] % mod; }
signed main() {
STCLOCK
io::read(n); for (int i = 1; i <= n; ++i) s += io::read(a[i]), ++ton[a[i]];
inv[1] = jc[0] = jc[1] = inj[0] = inj[1] = 1;
for (int i = 2; i <= N; ++i) {
inv[i] = inv[i - mod % i] * (mod / i + 1) % mod;
jc[i] = jc[i - 1] * i % mod;
inj[i] = inj[i - 1] * inv[i] % mod;
}
for (int i = 1; i <= N; ++i) coef = coef * inj[ton[i]] % mod;
S1[0][0] = S2[0][0] = 1;
for (int i = 1; i <= N; ++i)
for (int j = 1; j <= i; ++j) {
S1[i][j] = (S1[i - 1][j - 1] + S1[i - 1][j] * (i - 1)) % mod;
S2[i][j] = (S2[i - 1][j - 1] + S2[i - 1][j] * j) % mod;
}
f[0][0] = coef;
for (int i = 0; i < n; ++i)
for (int j = 0; j <= i; ++j) {
f[i + 1][j + 1] = (f[i + 1][j + 1] + f[i][j] * (a[i + 1] + 1)) % mod;
f[i + 1][j] = (f[i + 1][j] + f[i][j] * (s + i + 1 - j)) % mod;
}
for (int i = 0; i <= n; ++i) H[i] = f[n][i];
for (int i = n; i >= 0; --i)
for (int j = i; j <= n; ++j)
G[i] = (G[i] + H[j] * S1[j][i]) % mod;
for (int i = n; i >= 0; --i)
for (int j = i + 1; j <= n; ++j)
G[i] = (G[i] + mod - G[j] * C(j, i) % mod) % mod;
for (int i = n; i >= 0; --i) {
for (int j = i; j <= n; ++j)
F[i] = (F[i] + G[j] * S2[j][i]) % mod;
F[i] = F[i] * jc[i] % mod;
}
for (int i = n; i >= 1; --i)
for (int j = i + 1; j <= n; ++j)
F[i] = (F[i] + mod - F[j] * C(j - 1, i - 1) % mod) % mod;
for (int i = 1; i <= n; ++i)
io::write(F[i]), putchar('\n');
TIMENOW
return 0;
}
D3T3
Code
MX-NOI#1
T3 - box
考虑铁盒构成了一棵树的形式。随后发现,可以找到一个位置的最靠左时包含它的最深铁盒,最靠右时同理。然后考虑最深的必定包含它的铁盒应该是这两个铁盒在树上的 LCA。
时间复杂度 \(O(n\log_2 n)\),不要使用倍增求 LCA。
Code
// Go in my style.
// Not afraid to dark.
#include <bits/stdc++.h>
using namespace std;
clock_t sttime;
#define STCLOCK sttime = clock();
#define TIMENOW fprintf(stderr, "\nNOW TIME COSSEMED: %0.4lf\n", 1.0 * (clock() - sttime) / CLOCKS_PER_SEC);
#define inline __inline__ __attribute__ ((always_inline))
class IO
{
char ibuf[ 1 << 16 ], obuf[ 1 << 16 ], *ipl = ibuf, *ipr = ibuf, *op = obuf;
public:
~IO()
{
write();
}
void read()
{
if ( ipl == ipr )
ipr = ( ipl = ibuf ) + fread( ibuf, 1, 1 << 15, stdin );
}
void write()
{
fwrite( obuf, 1, op - obuf, stdout ), op = obuf;
}
char getchar()
{
return ( read(), ipl != ipr ) ? *ipl++ : EOF;
}
void putchar( char c )
{
*op++ = c;
if ( op - obuf > ( 1 << 15 ) )
write();
}
template < typename V > IO& operator>>( V& v )
{
int s = 1;
char c = getchar();
v = 0;
for ( ; !isdigit( c ); c = getchar() )
if ( c == '-' )
s = -s;
for ( ; isdigit( c ); c = getchar() )
v = ( v << 1 ) + ( v << 3 ) + ( c ^ 48 );
return v *= s, *this;
}
IO& operator<<( char c )
{
return putchar( c ), *this;
}
template < typename V > IO& operator<<( V v )
{
if ( !v )
putchar( '0' );
if ( v < 0 )
putchar( '-' ), v = -v;
char stk[ 100 ], *top = stk;
for ( ; v; v /= 10 )
*++top = v % 10 + '0';
while ( top != stk )
putchar( *top-- );
return *this;
}
} io;
const int N = 2e6, E = N;
int n, m, L, avt[N + 5];
struct bx { int l, r, i; bx(int L, int R, int I) { l = L, r = R, i = I; } };
vector<bx> a, b;
struct op { int o, i; op(int O, int I) { o = O, i = I; } };
vector<op> ot;
int cnt, rep[E + 5], fa[E + 5], sz[E + 5], dep[E + 5], rp[E + 5], st[25][E * 2 + 5], idx;
vector<int> to[E + 5];
namespace sgt {
#define lson(p) ((p) << 1)
#define rson(p) ((p) << 1 | 1)
const int P = E << 2;
int lt[P], rt[P], lb[P], rb[P], tag[P];
inline void put_tag(int p, int x) { lb[p] += x, rb[p] += x, tag[p] += x; }
inline void push_up(int p) { lb[p] = min(lb[lson(p)], lb[rson(p)]) + tag[p], rb[p] = max(rb[lson(p)], rb[rson(p)]) + tag[p]; }
void Build(int p, int l, int r) {
lt[p] = l, rt[p] = r, tag[p] = 0;
lb[p] = L + 1, rb[p] = 0;
if (l == r) return ;
int mid = (l + r) >> 1;
Build(lson(p), l, mid), Build(rson(p), mid + 1, r);
}
int Quex(int p, int x) {
if (lb[p] > x || rb[p] < x) return 0;
if (lt[p] == rt[p]) return lt[p];
int i = Quex(rson(p), x - tag[p]);
if (!i) i = Quex(lson(p), x - tag[p]);
return i;
}
void Actl(int p, int x) {
if (lt[p] == rt[p]) {
if (!lt[p]) return lb[p] = 1, rb[p] = L, void();
lb[p] = 1 + tag[p];
rb[p] = lb[p] - b[lt[p]].l + b[lt[p]].r;
return ;
}
int mid = (lt[p] + rt[p]) >> 1;
if (x <= mid) Actl(lson(p), x);
else Actl(rson(p), x);
push_up(p);
}
void Actr(int p, int x) {
if (lt[p] == rt[p]) {
if (!lt[p]) return lb[p] = 1, rb[p] = L, void();
int i = b[lt[p]].i;
rb[p] = L + tag[p];
lb[p] = rb[p] - b[lt[p]].r + b[lt[p]].l;
return ;
}
int mid = (lt[p] + rt[p]) >> 1;
if (x <= mid) Actr(lson(p), x);
else Actr(rson(p), x);
push_up(p);
}
void Upd(int p, int l, int r, int x) {
if (l <= lt[p] && rt[p] <= r) return put_tag(p, x);
int mid = (lt[p] + rt[p]) >> 1;
if (l <= mid) Upd(lson(p), l, r, x);
if (r > mid) Upd(rson(p), l, r, x);
push_up(p);
}
#undef lson
#undef rson
}
inline void Activl(int i) {
sgt::Actl(1, i);
if (!i) return ;
int l, r;
l = i + sz[b[i].i], r = rep[fa[b[i].i]] + sz[fa[b[i].i]] - 1;
if (l <= r) sgt::Upd(1, l, r, b[i].r - b[i].l + 2);
}
inline void Activr(int i) {
sgt::Actr(1, i);
if (!i) return ;
int l, r;
l = i + sz[b[i].i], r = rep[fa[b[i].i]] + sz[fa[b[i].i]] - 1;
if (l <= r) sgt::Upd(1, l, r, -(b[i].r - b[i].l + 2));
}
void init(int u, int father) {
st[0][rp[u] = ++idx] = u;
sz[u] = 1;
for (int v : to[u]) if (v != father) {
dep[v] = dep[u] + 1;
init(v, u);
sz[u] += sz[v];
st[0][++idx] = u;
}
}
inline int mindep(int x, int y) { return dep[x] < dep[y] ? x : y; }
#define log2_(x) (31 ^ __builtin_clz(x))
inline int LCA(int u, int v) {
u = rp[u], v = rp[v];
if (u > v) swap(u, v);
static int p; p = log2_(v - u + 1);
return mindep(st[p][u], st[p][v - (1 << p) + 1]);
}
signed main() {
STCLOCK
freopen("box.in", "r", stdin);
freopen("box.out", "w", stdout);
io >> n >> m >> L;
a.emplace_back(1, L, 0);
b.emplace_back(1, L, 0);
for (int i = 1, l, r; i <= n; ++i) {
io >> l >> r;
a.emplace_back(l, r, i);
b.emplace_back(l, r, i);
}
for (int i = 1, o, l, r; i <= m; ++i) {
io >> o >> l;
if (o == 1) {
io >> r;
b.emplace_back(l, r, n + i);
l = n + i;
}
ot.emplace_back(o, l);
}
{
sort(a.begin(), a.end(), [](const bx &a, const bx &b) { return pair{pair{a.l, -a.r}, a.i} < pair{pair{b.l, -b.r}, b.i}; });
sort(b.begin(), b.end(), [](const bx &a, const bx &b) { return pair{pair{a.l, -a.r}, a.i} < pair{pair{b.l, -b.r}, b.i}; });
cnt = 0; stack<bx> tr;
for (bx x : b) {
rep[x.i] = cnt++;
while (!tr.empty() && tr.top().r < x.l) tr.pop();
if (!tr.empty()) fa[x.i] = tr.top().i, to[tr.top().i].emplace_back(x.i);
tr.emplace(x);
}
init(0, -1);
for (int i = 1; i <= 21; ++i)
for (int j = 1; j <= idx; ++j) {
st[i][j] = st[i - 1][j];
if (j + (1 << (i - 1)) <= idx) st[i][j] = mindep(st[i][j], st[i - 1][j + (1 << (i - 1))]);
}
sgt::Build(1, 0, cnt - 1);
for (bx x : a) Activl(rep[x.i]);
int tt = 0;
for (op q : ot)
if (q.o == 1) Activl(rep[q.i]);
else avt[++tt] = b[sgt::Quex(1, q.i)].i;
}
{
sort(a.begin(), a.end(), [](const bx &a, const bx &b) { return pair{pair{-a.r, a.l}, a.i} < pair{pair{-b.r, b.l}, b.i}; });
sort(b.begin(), b.end(), [](const bx &a, const bx &b) { return pair{pair{-a.r, a.l}, a.i} < pair{pair{-b.r, b.l}, b.i}; });
cnt = 0;
for (bx x : b) rep[x.i] = cnt++;
sgt::Build(1, 0, cnt - 1);
for (bx x : a) Activr(rep[x.i]);
int tt = 0;
if (1.0 * (clock() - sttime) / CLOCKS_PER_SEC >= 3.0) exit(1);
for (op q : ot)
if (q.o == 1) Activr(rep[q.i]);
else io << dep[LCA(avt[++tt], b[sgt::Quex(1, q.i)].i)] << '\n';
}
TIMENOW
return 0;
}
MX-NOI#16
T3 - point
才知道原来吉司机线段树可以求区间历史版本和啊。
考虑一个简单的事实是,对于 \(y\),一定有一个分界点 \(dv_y\),满足这一列上所有 \(x< dv_y\) 的 \(x\) 由 \(x=1\) 的操作贡献,而 \(x\ge dv_y\) 的 \(x\) 由 \(x=n\) 的操作贡献。
可以使用某些方式求出 \(dv_y\),较简单的一种是线段树上二分。
那么就可以将 \(x=1\) 和 \(x=n\) 的操作分离。之后需要维护的操作是:激活一个点,对区间上的数取 \(\max\)、查询一个区间的历史版本和。这个可以使用势能线段树维护。
时间复杂度 \(O(n\log_2 n)\)。
Code
// Go in my style.
// Not afraid to dark.
#include <bits/stdc++.h>
using namespace std;
clock_t sttime;
#define STCLOCK sttime = clock();
#define TIMENOW fprintf(stderr, "\nNOW TIME COSSEMED: %0.4lf\n", 1.0 * (clock() - sttime) / CLOCKS_PER_SEC);
#define inline __inline__ __attribute__ ((always_inline))
#define ll long long
namespace io {
int pos;
inline int read(int &p = pos) {
static bool v; static char c;
p = 0, v = false, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = true;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v ? - p : p;
}
inline void write(ll x) {
static int sta[65], top; top = 0;
if (x < 0) putchar('-'), x = - x;
do sta[++top] = x % 10, x /= 10;
while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int N = 1e6;
int n, m;
struct op {
int ty, x, ly, ry, v;
inline void re() {
int lx, rx; io::read(lx), io::read(rx);
ty = lx != 1; x = ty ? lx : rx;
io::read(ly), io::read(ry), io::read(v);
}
} O[N + 5];
namespace get_dv {
multiset<int> sl[N + 5], sr[N + 5]; // l : lmax,0 ; r : rmax,1
namespace sgt {
#define lson(p) ((p) << 1)
#define rson(p) ((p) << 1 | 1)
const int P = N << 2 | 3;
int lt[P], rt[P], lmx[P], rmx[P];
void Build(int p, int l, int r){
lt[p] = l, rt[p] = r, lmx[p] = rmx[p] = 0;
if (l == r) return ;
int mid = (l + r) >> 1;
Build(lson(p), l, mid), Build(rson(p), mid + 1, r);
}
void Update(int p, int x, int v, int *mx) {
if (lt[p] == rt[p]) return mx[p] = v, void();
int mid = (lt[p] + rt[p]) >> 1;
if (x <= mid) Update(lson(p), x, v, mx);
else Update(rson(p), x, v, mx);
mx[p] = max(mx[lson(p)], mx[rson(p)]);
}
int find_dv(int p, int Lmx, int Rmx) {
if (lt[p] == rt[p]) return lt[p];
int tmpl = max(Lmx, lmx[rson(p)]), tmpr = max(Rmx, rmx[lson(p)]);
if (tmpl > tmpr) return find_dv(rson(p), Lmx, tmpr);
return find_dv(lson(p), tmpl, Rmx);
}
#undef lson
#undef rson
}
int dv[N + 5];
struct op1 {
int ty, x, y, v;
} td[N * 2 + 5];
} using get_dv::dv;
ll ans[N + 5];
struct que { int lx, rx, ly, ry, i; } q[N + 5];
namespace solu {
ll T;
namespace sgt {
#define lson(p) ((p) << 1)
#define rson(p) ((p) << 1 | 1)
const int P = N << 2 | 3, inf = N + 5;
int lt[P], rt[P], mn0[P], mn[P], omn[P], mnc[P], mtag[P];
ll dtag[P], sum[P], tot[P]; //dtag[p] : [lt[p], rt[p]]区间内所有等于 mn[p] 的位置减去 dtag[p]
void Build(int p, int l, int r) {
lt[p] = l, rt[p] = r;
mn0[p] = inf;
mn[p] = omn[p] = -1;
mnc[p] = dtag[p] = mtag[p] = sum[p] = tot[p] = 0;
if (l == r) return ;
int mid = (l + r) >> 1;
Build(lson(p), l, mid), Build(rson(p), mid + 1, r);
}
inline void put_dtag(int p, ll x, int nw) {
dtag[p] += x;
tot[p] -= x * mnc[p];
int d = max(mn[p], nw) - mn[p];
sum[p] += 1ll * d * mnc[p];
mtag[p] = mn[p] = mn[p] + d;
}
inline void push_up(int p) {
if (mn[lson(p)] == -1) {
mn[p] = mn[rson(p)], mn0[p] = mn0[rson(p)];
mnc[p] = mnc[rson(p)];
} else
if (mn[rson(p)] == -1) {
mn[p] = mn[lson(p)], mn0[p] = mn0[lson(p)];
mnc[p] = mnc[lson(p)];
} else
if (mn[lson(p)] < mn[rson(p)]) {
mn[p] = mn[lson(p)];
mn0[p] = min(mn0[lson(p)], mn[rson(p)]);
mnc[p] = mnc[lson(p)];
} else
if (mn[lson(p)] > mn[rson(p)]) {
mn[p] = mn[rson(p)];
mn0[p] = min(mn0[rson(p)], mn[lson(p)]);
mnc[p] = mnc[rson(p)];
} else {
mn[p] = mn[lson(p)];
mn0[p] = min(mn0[lson(p)], mn0[rson(p)]);
mnc[p] = mnc[lson(p)] + mnc[rson(p)];
}
omn[p] = mn[p];
sum[p] = sum[lson(p)] + sum[rson(p)];
tot[p] = tot[lson(p)] + tot[rson(p)];
}
void put_mtag(int p, int v) {
if (v <= mn[p]) return ;
if (mn[p] < 0) return mtag[p] = max(mtag[p], v), void();
if (mn[p] < v && v < mn0[p])
put_dtag(p, T * (v - mn[p]), v);
else {
if (omn[p] == mn[lson(p)]) put_dtag(lson(p), dtag[p], mtag[p]);
if (omn[p] == mn[rson(p)]) put_dtag(rson(p), dtag[p], mtag[p]);
put_mtag(lson(p), v), put_mtag(rson(p), v);
push_up(p);
dtag[p] = mtag[p] = 0;
}
}
inline void push_down(int p) {
if (omn[p] == mn[lson(p)]) put_dtag(lson(p), dtag[p], mtag[p]);
if (omn[p] == mn[rson(p)]) put_dtag(rson(p), dtag[p], mtag[p]);
if (mtag[p] > 0) put_mtag(lson(p), mtag[p]), put_mtag(rson(p), mtag[p]);
push_up(p);
dtag[p] = mtag[p] = 0;
}
void acti(int p, int x) {
if (lt[p] == rt[p]) {
omn[p] = mn[p] = sum[p] = mtag[p], mnc[p] = 1;
tot[p] = - T * sum[p];
return ;
}
push_down(p);
int mid = (lt[p] + rt[p]) >> 1;
if (x <= mid) acti(lson(p), x);
else acti(rson(p), x);
push_up(p);
}
void upda(int p, int l, int r, int v) {
if (l <= lt[p] && rt[p] <= r) return put_mtag(p, v);
push_down(p);
int mid = (lt[p] + rt[p]) >> 1;
if (l <= mid) upda(lson(p), l, r, v);
if (r > mid) upda(rson(p), l, r, v);
push_up(p);
}
ll quer(int p, int l, int r) {
if (l <= lt[p] && rt[p] <= r) return tot[p] + T * sum[p];
push_down(p);
int mid = (lt[p] + rt[p]) >> 1; ll res = 0;
if (l <= mid) res += quer(lson(p), l, r);
if (r > mid) res += quer(rson(p), l, r);
return res;
}
#undef lson
#undef rson
}
vector<op> oo;
struct pst { int x, y; };
vector<pst> pp;
struct tmpque { int x, ly, ry, i; ll v; };
vector<tmpque> qq;
inline void sol() {
pp.clear(); for (int y = 1; y <= n; ++y) pp.emplace_back(pst{dv[y], y});
qq.clear(); for (int i = 1; i <= m; ++i)
qq.emplace_back(tmpque{q[i].lx - 1, q[i].ly, q[i].ry, q[i].i, -1ll}),
qq.emplace_back(tmpque{q[i].rx, q[i].ly, q[i].ry, q[i].i, 1ll});
sort(oo.begin(), oo.end(), [](const op &a, const op &b) { return a.x < b.x; });
sort(pp.begin(), pp.end(), [](const pst &a, const pst &b) { return a.x < b.x; });
sort(qq.begin(), qq.end(), [](const tmpque &a, const tmpque &b) { return a.x < b.x; });
int Io = 0, Ip = 0, Iq = 0;
sgt::Build(1, 1, n);
T = 0;
for (int x = 0; x <= n; ++x) {
using namespace sgt;
while (Ip < (int)pp.size() && pp[Ip].x <= x) acti(1, pp[Ip++].y);
while (Io < (int)oo.size() && oo[Io].x <= x) upda(1, oo[Io].ly, oo[Io].ry, oo[Io].v), Io++;
++T;
while (Iq < (int)qq.size() && qq[Iq].x <= x) ans[qq[Iq].i] += qq[Iq].v * quer(1, qq[Iq].ly, qq[Iq].ry), Iq++;
}
}
}
inline int &rev(int &x) { return x = n - x + 1; }
signed main() {
STCLOCK
freopen("point.in", "r", stdin);
freopen("point.out", "w", stdout);
io::read(n), io::read(m);
{
using namespace get_dv;
using namespace sgt;
for (int i = 1; i <= n; ++i) {
O[i].re();
td[i] = op1{O[i].ty, O[i].x, O[i].ly, O[i].v};
td[i + n] = op1{O[i].ty, O[i].x, O[i].ry + 1, -O[i].v};
}
sort(td + 1, td + n * 2 + 1, [](const op1 &a, const op1 &b) { return a.y != b.y ? a.y < b.y : a.v < b.v; });
Build(1, 0, n + 1);
for (int y = 1, id = 1; y <= n; ++y) {
while (id <= n * 2 && td[id].y <= y) {
auto &o = td[id++];
int *mx; multiset<int> *s;
if (o.ty) mx = rmx, s = sr;
else mx = lmx, s = sl, ++o.x;
if (o.v > 0) s[o.x].insert(o.v);
else s[o.x].erase(s[o.x].find(-o.v));
Update(1, o.x, s[o.x].empty() ? 0 : *--s[o.x].end(), mx);
}
dv[y] = find_dv(1, 0, 0);
}
}
{
for (int i = 1; i <= m; ++i)
io::read(q[i].lx), io::read(q[i].rx), io::read(q[i].ly), io::read(q[i].ry), q[i].i = i;
using namespace solu;
for (int i = 1; i <= n; ++i) if (O[i].ty) oo.emplace_back(O[i]);
sol();
oo.clear();
for (int i = 1; i <= n; ++i) if (!O[i].ty) oo.emplace_back(O[i]), rev(oo.back().x);
for (int i = 1; i <= m; ++i) swap(rev(q[i].lx), rev(q[i].rx));
for (int i = 1; i <= n; ++i) rev(dv[i])++;
sol();
}
for (int i = 1; i <= m; ++i) io::write(ans[i]), putchar('\n');
TIMENOW
return 0;
}
MX-NOI#18
T3 - match
考虑一个矩阵 \(M_{ij}=e_{ij}\cdot x_{ij}\),其中 \(x_{ij}\) 当作一个未知元。那么 \(e\) 存在完美匹配等价于 \(M_{ij}\) 的行列式(一个多项式)不等于 \(0\)。
根据 Schwartz-Zippel 引理,给一个多元多项式 \(f\) 的每个元随机赋值得到多项式的点值为 \(w\), 那么 \(\Pr\left[[f\ne 0] \land[w=0]\right]=\frac{d}{|S|}\),其中 \(|S|\) 是值域大小,\(d\) 是 \(f\) 的最高此项。于是给 \(x_{ij}\) 随机赋值,然后检验 \(\det M\) 是否等于 \(0\) 正确的概率很高。
现在考虑颜色的集合。那么额外加入一个元 \(y\) 做子集并卷积,那么 \(M_{ij}=e_{ij}\cdot x_{ij}\cdot y^{2^{c_{ij}}}\),然后对于每个集合 \(S\),考虑最终多项式中 \(y^S\) 的系数是否为 \(0\) 就行。
求值可以考虑先 FWT 然后对每个集合分别求出行列式然后再 IFWT。
那么现在再考虑颜色的变化,\(M_{ij}=e_{ij}\cdot x_{ij}\cdot(y^{2^{c_{ij}}}+z\cdot(y^{2^{c_{ij}-1}}+y^{2^{c_{ij}+1}}))\)。那么就是把上面 FWT 和求行列式时的数变成了一个 \(az+b\) 的一元一次多项式,然后在过程中对 \(z^2\) 取模即可。那么由于 \(az+b\) 和 \(-\frac{a}{b^2} \cdot z + \frac{1}{b}\) 互逆,对于一些情况特殊处理之后,发现行列式也是可以求的。
时间复杂度 \(O(n^32^k)\)。
Code
// Go in my style.
// Not afraid to dark.
#include <bits/stdc++.h>
using namespace std;
clock_t sttime;
#define STCLOCK sttime = clock();
#define TIMENOW fprintf(stderr, "\nNOW TIME COSSEMED: %0.4lf\n", 1.0 * (clock() - sttime) / CLOCKS_PER_SEC);
#define inline __inline__ __attribute__ ((always_inline))
#define int long long
namespace io {
int pos;
inline int read(int &p = pos) {
static bool v; static char c;
p = 0, v = false, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = true;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v ? - p : p;
}
inline void write(int x) {
static int sta[65], top; top = 0;
if (x < 0) putchar('-'), x = - x;
do sta[++top] = x % 10, x /= 10;
while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int N = 50, C = 10, mod = 998244353;
int n, m, cn, c[N + 5][N + 5], rx[N + 5][N + 5];
bool exi[N + 5][N + 5];
#define pre(x) (((x) - 1 + cn) % cn)
#define nex(x) (((x) + 1) % cn)
inline int power(int a, int p) {
static int res; res = 1;
while (p > 0) {
if (p & 1) res = res * a % mod;
a = a * a % mod, p >>= 1;
}
return res;
}
inline int invn(int v) { return power(v, mod - 2); }
struct poly {
int a, b;
inline friend poly operator+ (const poly x, const poly y) { return poly((x.a + y.a) % mod, (x.b + y.b) % mod); }
inline friend poly operator- (const poly x, const poly y) { return poly((x.a - y.a + mod) % mod, (x.b - y.b + mod) % mod); }
inline friend poly operator* (const poly x, const poly y) {
return poly((x.a * y.b + x.b * y.a) % mod, x.b * y.b % mod);
}
inline poly inv() {
int ib = invn(b);
return poly((mod - a * ib % mod * ib % mod) % mod, ib);
}
poly(int A = 0, int B = 0) { a = A, b = B; }
} np[N + 5][N + 5];
poly res[1 << C];
mt19937 mt(980619);
int ans[1 << C];
inline void FWT(poly *f, int len, int o) {
for (int l = 2, md; l <= len; l <<= 1) {
md = l >> 1;
for (int i = 0; i < len; i += l)
for (int j = i; j < i + md; ++j)
f[j + md] = o == 1 ? (f[j + md] + f[j]) : (f[j + md] - f[j]);
}
}
inline int contr(int x, int y) { return (x & y) == y; }
inline poly det() {
poly r = poly(0, 1);
for (int i = 1; i <= n; ++i) {
if (np[i][i].b == 0)
for (int j = i + 1; j <= n; ++j)
if (np[j][i].b != 0) {
for (int k = i; k <= n; ++k)
swap(np[i][k], np[j][k]);
r.b = mod - r.b;
break;
}
if (np[i][i].b == 0)
for (int j = i + 1; j <= n; ++j)
if (np[j][i].a != 0) {
for (int k = i; k <= n; ++k)
swap(np[i][k], np[j][k]);
r.b = mod - r.b;
break;
}
poly iv;
if (np[i][i].b == 0) {
if (np[i][i].a == 0) return poly(0, 0);
iv = poly(0, invn(np[i][i].a));
for (int j = i + 1; j <= n; ++j) {
poly tmp = iv * poly(0, np[j][i].a);
for (int k = n; k >= i; --k)
np[j][k] = np[j][k] - (tmp * np[i][k]);
}
} else {
iv = np[i][i].inv();
for (int j = i + 1; j <= n; ++j) {
poly tmp = iv * np[j][i];
for (int k = n; k >= i; --k)
np[j][k] = np[j][k] - (tmp * np[i][k]);
}
}
}
for (int i = 1; i <= n; ++i)
r = r * np[i][i];
return r;
}
inline void ARKNIGHTS() {
for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j)
rx[i][j] = mt() % mod;
for (int ns = 0; ns < (1 << cn); ++ns) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
if (!exi[i][j]) np[i][j] = poly(0, 0);
else {
np[i][j].b = contr(ns, 1 << c[i][j]);
np[i][j].a = contr(ns, 1 << pre(c[i][j])) + contr(ns, 1 << nex(c[i][j]));
np[i][j] = np[i][j] * poly(0, rx[i][j]);
}
}
res[ns] = det();
}
FWT(res, 1 << cn, -1);
for (int i = 0; i < (1 << cn); ++i)
ans[i] |= (res[i].a != 0 || res[i].b != 0);
}
signed main() {
STCLOCK
freopen("match.in", "r", stdin);
freopen("match.out", "w", stdout);
io::read(n), io::read(m), io::read(cn);
for (int i = 1, u, v, l; i <= m; ++i) {
io::read(u), io::read(v), io::read(l), --l;
c[u][v] = l; exi[u][v] = true;
}
for (int T = 2; T--; ARKNIGHTS()) ;
for (int i = 0; i < (1 << cn); ++i) putchar(ans[i] + '0');
TIMENOW
return 0;
}
Miscellaneous
Luogu P7470 - [NOI Online 2021 提高组] 岛屿探险
考虑限制 \(a_i\oplus c \le \min(b_i,d)\) 可以拆成 \(a_i\oplus c \le b_i\) 以及 \(a_i\oplus c\le d\) 两个不同的限制。然后可以分成 \(b_i<d\) 和 \(b_i\ge d\) 两部分来求,发现两部分均可以 cdq 分治,于是就做完了,时间复杂度 \(O(n\log_2^2 n)\)。
Code
// Go in my style.
// Not afraid to dark.
#include <bits/stdc++.h>
using namespace std;
clock_t sttime;
#define STCLOCK sttime = clock();
#define TIMENOW fprintf(stderr, "\nNOW TIME COSSEMED: %0.4lf\n", 1.0 * (clock() - sttime) / CLOCKS_PER_SEC);
#define inline __inline__ __attribute__ ((always_inline))
namespace io {
int pos;
inline int read(int &p = pos) {
static bool v; static char c;
p = 0, v = false, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = true;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v ? - p : p;
}
inline void write(int x) {
static int sta[65], top; top = 0;
if (x < 0) putchar('-'), x = - x;
do sta[++top] = x % 10, x /= 10;
while (x);
while (top) putchar(sta[top--] + 48);
}
}
const int N = 1e5, Bi = 24;
int n, Q, ans[N + 5];
int cnt;
struct node {
int x, y, i, p, q;
node(int _x_ = 0, int _y_ = 0, int _i_ = 0, int _p_ = 0, int _q_ = 0) { x = _x_, y = _y_, i = _i_, p = _p_, q = _q_; }
} ax[N * 3 + 5];
auto cmp1 = [](const node &x, const node &y) { return x.y != y.y ? x.y > y.y : x.i < y.i; };
auto cmp2 = [](const node &x, const node &y) { return x.y != y.y ? x.y < y.y : x.i > y.i; };
int tot;
struct tri {
int son[2], cnt;
tri() { son[0] = son[1] = cnt = 0; }
} tr[N * Bi];
inline int to(int u, int e) { if (!tr[u].son[e]) tr[u].son[e] = ++tot; return tr[u].son[e]; }
void Ins(int u, int w, int e = Bi - 1) {
++tr[u].cnt;
if (e == -1) return ;
Ins(to(u, w >> e & 1), w, e - 1);
}
int Cnt(int u, int x, int y, int e = Bi - 1) {
if (e == -1) return tr[u].cnt;
int xe = x >> e & 1, ye = y >> e & 1, res = 0;
if (ye == 1) {
if (tr[u].son[xe]) res += tr[tr[u].son[xe]].cnt;
if (tr[u].son[xe ^ 1]) res += Cnt(tr[u].son[xe ^ 1], x, y, e - 1);
} else if (tr[u].son[xe])
res += Cnt(tr[u].son[xe], x, y, e - 1);
return res;
}
void _Ins(int u, int x, int y, int e = Bi - 1) {
if (e == -1) return ++tr[u].cnt, void();
int xe = x >> e & 1, ye = y >> e & 1;
if (ye == 1) {
++tr[to(u, xe)].cnt;
_Ins(to(u, xe ^ 1), x, y, e - 1);
} else _Ins(to(u, xe), x, y, e - 1);
}
int _Cnt(int u, int w, int e = Bi - 1) {
if (e == -1) return tr[u].cnt;
int res = tr[u].cnt;
if (tr[u].son[w >> e & 1]) res += _Cnt(tr[u].son[w >> e & 1], w, e - 1);
return res;
}
inline void Clear() {
for (; tot >= 0; --tot) tr[tot] = tri();
tot = 0;
}
void sol(int l, int r) {
if (l == r) return ;
int mid = (l + r) >> 1, idl, idr;
sol(l, mid), sol(mid + 1, r);
sort(ax + l, ax + mid + 1, cmp1);
sort(ax + mid + 1, ax + r + 1, cmp1);
idl = l, idr = mid + 1;
while (idl <= mid && idr <= r)
if (cmp1(ax[idl], ax[idr])) {
if (!ax[idl].i) Ins(0, ax[idl].x);
++idl;
} else {
if (ax[idr].i) ans[ax[idr].i] += ax[idr].q * Cnt(0, ax[idr].x, ax[idr].y);
++idr;
}
while (idr <= r) {
if (ax[idr].i) ans[ax[idr].i] += ax[idr].q * Cnt(0, ax[idr].x, ax[idr].y);
++idr;
}
Clear();
sort(ax + l, ax + mid + 1, cmp2);
sort(ax + mid + 1, ax + r + 1, cmp2);
idl = l, idr = mid + 1;
while (idl <= mid && idr <= r)
if (cmp2(ax[idl], ax[idr])) {
if (!ax[idl].i) _Ins(0, ax[idl].x, ax[idl].y);
++idl;
} else {
if (ax[idr].i) ans[ax[idr].i] += ax[idr].q * _Cnt(0, ax[idr].x);
++idr;
}
while (idr <= r) {
if (ax[idr].i) ans[ax[idr].i] += ax[idr].q * _Cnt(0, ax[idr].x);
++idr;
}
Clear();
}
signed main() {
STCLOCK
io::read(n), io::read(Q);
for (int i = 1, a, b; i <= n; ++i) {
io::read(a), io::read(b);
ax[++cnt] = node(a, b, 0, i, 0);
}
for (int i = 1, l, r, c, d; i <= Q; ++i) {
io::read(l), io::read(r), io::read(c), io::read(d);
ax[++cnt] = node(c, d, i, r, 1);
ax[++cnt] = node(c, d, i, l - 1, -1);
}
sort(ax + 1, ax + cnt + 1, [](const node &x, const node &y) { return x.p != y.p ? x.p < y.p : x.i < y.i; });
// for (int i = 1; i <= cnt; ++i) printf("%d %d %d %d %d\n", ax[i].x, ax[i].y, ax[i].i, ax[i].p, ax[i].q);
sol(1, cnt);
for (int i = 1; i <= Q; ++i) io::write(ans[i]), putchar('\n');
TIMENOW
return 0;
}
Luogu P9068 - [Ynoi Easy Round 2022] 超人机械 TEST_95
考虑对于每个 \(x\),求出 \(a(x)\) 表示 \(x\) 的最靠前的位置,\(b(x)\) 表示 \(x\) 的最靠后的位置。那么对于一个 \(x>y\) ,只要是 \(a(x)<b(y)\) 那么就有贡献。然后就可以扫描线求。发现对于一个修改,只有最多四个 \(a(x)/a(y)/b(x)/b(y)\) 会更新。所以可以 cdq 分治,时间复杂度为 \(O(n\log_2^2 n)\)。
Code
// Go in my style.
// Not afraid to dark.
#include <bits/stdc++.h>
using namespace std;
clock_t sttime;
#define STCLOCK sttime = clock();
#define TIMENOW fprintf(stderr, "\nNOW TIME COSSEMED: %0.4lf\n", 1.0 * (clock() - sttime) / CLOCKS_PER_SEC);
#define inline __inline__ __attribute__ ((always_inline))
namespace io {
static char buf[1000000], *p1 = buf, *p2 = buf;
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++
int pos;
inline int read(int &p = pos) {
static bool v; static char c;
p = 0, v = false, c = getchar();
while (!isdigit(c)) {
if (c == '-') v = true;
c = getchar();
}
while (isdigit(c)) {
p = (p << 1) + (p << 3) + (c - 48);
c = getchar();
}
return p = v ? - p : p;
}
char pbuf[1 << 20], *pp = pbuf;
void putc(const char &c) {
if (pp - pbuf == 1 << 20) fwrite(pbuf, 1, 1 << 20, stdout), pp = pbuf;
*pp++ = c;
}
inline void write(long long x) {
static int sta[65], top; top = 0;
if (x < 0) putchar('-'), x = - x;
do sta[++top] = x % 10, x /= 10;
while (x);
while (top) putc(sta[top--] + 48);
}
struct PUT {
~PUT() {
fwrite(pbuf, 1, pp - pbuf, stdout);
}
} PP;
}
const int N = 1e5;
int n, Q, a[N + 5];
set<int> posi[N + 5];
int cnt;
struct node {
int x, p, i, ty, o;
node(int _x_ = 0, int _p_ = 0, int _i_ = 0, int _ty_ = 0, int _o_ = 0) { x = _x_, p = _p_, i = _i_, ty = _ty_, o = _o_; }
} ax[N * 10 + 5];
long long ans[N + 5];
inline void Add(int i, int x, int o) {
if (!posi[i].empty()) {
ax[++cnt] = node(i, *posi[i].begin(), x, 0, o);
ax[++cnt] = node(i, *posi[i].rbegin(), x, 1, o);
}
}
namespace BIT {
inline int lowbit(const int x) { return x & -x; }
int c[N + 5], tot;
inline void Upd(int p, int x) { tot += x; while (p <= n) c[p] += x, p += lowbit(p); }
inline int Que(int p) {
static int res; res = 0;
while (p > 0) res += c[p], p ^= lowbit(p);
return res;
}
inline void Clear(int p) { tot = 0; while (p <= n) c[p] = 0, p += lowbit(p); }
}
void sol(int l, int r) {
if (l == r) return ;
int mid = (l + r) >> 1, idl, idr, id;
sol(l, mid), sol(mid + 1, r);
sort(ax + l, ax + mid + 1, [](const node &a, const node &b) { return a.p < b.p; });
sort(ax + mid + 1, ax + r + 1, [](const node &a, const node &b) { return a.p < b.p; });
idl = l, idr = mid + 1; id = l;
while (idl <= mid && idr <= r)
if (ax[idl].p < ax[idr].p) {
if (ax[idl].ty == 0) BIT::Upd(ax[idl].x, ax[idl].o);
++idl;
} else {
if (ax[idr].ty == 1) ans[ax[idr].i] += ax[idr].o * (BIT::tot - BIT::Que(ax[idr].x));
++idr;
}
while (idr <= r) {
if (ax[idr].ty == 1) ans[ax[idr].i] += ax[idr].o * (BIT::tot - BIT::Que(ax[idr].x));
++idr;
}
for (int i = l; i <= idl; ++i) if (ax[i].ty == 0) BIT::Clear(ax[i].x);
reverse(ax + l, ax + mid + 1);
reverse(ax + mid + 1, ax + r + 1);
// sort(ax + l, ax + mid + 1, [](const node &a, const node &b) { return a.p > b.p; });
// sort(ax + mid + 1, ax + r + 1, [](const node &a, const node &b) { return a.p > b.p; });
idl = l, idr = mid + 1;
while (idl <= mid && idr <= r)
if (ax[idl].p > ax[idr].p) {
if (ax[idl].ty == 1) BIT::Upd(ax[idl].x, ax[idl].o);
++idl;
} else {
if (ax[idr].ty == 0) ans[ax[idr].i] += ax[idr].o * BIT::Que(ax[idr].x - 1);
++idr;
}
while (idr <= r) {
if (ax[idr].ty == 0) ans[ax[idr].i] += ax[idr].o * BIT::Que(ax[idr].x - 1);
++idr;
}
for (int i = l; i <= idl; ++i) if (ax[i].ty == 1) BIT::Clear(ax[i].x);
}
signed main() {
STCLOCK
io::read(n); for (int i = 1; i <= n; ++i) posi[io::read(a[i])].insert(i);
for (int i = 1; i <= n; ++i) Add(i, 0, 1);
io::read(Q); for (int i = 1, x, y; i <= Q; ++i) {
io::read(x), io::read(y);
if (*posi[a[x]].begin() == x || *posi[a[x]].rbegin() == x) {
Add(a[x], i, -1);
posi[a[x]].erase(x);
Add(a[x], i, 1);
} else posi[a[x]].erase(x);
a[x] = y;
if (posi[a[x]].empty() || *posi[a[x]].begin() > x || *posi[a[x]].rbegin() < x) {
Add(a[x], i, -1);
posi[a[x]].insert(x);
Add(a[x], i, 1);
} else posi[a[x]].insert(x);
}
sol(1, cnt);
io::write(ans[0]), io::putc('\n');
for (int i = 1; i <= Q; ++i) io::write(ans[i] += ans[i - 1]), io::putc('\n');
TIMENOW
return 0;
}

浙公网安备 33010602011771号