20250204
T1
分糖果
考虑链,只需要线段树维护 dp 转移。考虑环,然后容斥,钦定最后一个人和第一个人一样。这样就相当于把最后一个人去掉,把第一个人的 \(a_i = \min \{a_i, a+n\}\),然后再做环上的问题。因此先把最小的人循环移位到第一个,这样容斥一直做下去就方便很多。最后相当于是所有前 \(i\) 个人的答案乘以 \(\pm 1\) 加起来。不能动开,要离散化。线段树区间归零、区间取反最好做成区间乘法,这样方便且常数小。
代码
#include <iostream>
#include <algorithm>
#include <time.h>
#define int long long
using namespace std;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
char buf[1<<21],*p1,*p2,ch;
int read(){
int ret=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')ret=ret*10+c-'0',c=getchar();
return ret;
}
const int P = 1000000007;
inline void Madd(int& x, int y) { (x += y) >= P ? (x -= P) : 0; }
int n, mp;
int a[2000005], v[2000005];
struct Segment_Tree {
int s[4000005], tga[4000005], tgm[4000005];
int sv[4000005];
inline void taga(int o, int v) {
s[o] = (s[o] + sv[o] * v) % P;
Madd(tga[o], v);
}
inline void tagm(int o, int v) {
tga[o] = tga[o] * v % P;
tgm[o] = tgm[o] * v % P;
s[o] = s[o] * v % P;
}
void pushdown(int o) {
if (tgm[o] != 1) {
tagm(o << 1, tgm[o]);
tagm(o << 1 | 1, tgm[o]);
tgm[o] = 1;
}
if (tga[o] != 0) {
taga(o << 1, tga[o]);
taga(o << 1 | 1, tga[o]);
tga[o] = 0;
}
}
inline void pushup(int o) { s[o] = (s[o << 1] + s[o << 1 | 1]) % P; }
void Build(int o, int l, int r) {
tgm[o] = 1;
if (l == r) {
sv[o] = v[l];
return;
}
int mid = (l + r) >> 1;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
sv[o] = sv[o << 1] + sv[o << 1 | 1];
}
void Add(int o, int l, int r, int L, int R, int v) {
if (L <= l && r <= R)
return taga(o, v);
pushdown(o);
int mid = (l + r) >> 1;
if (L <= mid)
Add(o << 1, l, mid, L, R, v);
if (R > mid)
Add(o << 1 | 1, mid + 1, r, L, R, v);
pushup(o);
}
void Mul(int o, int l, int r, int L, int R, int v) {
if (L <= l && r <= R)
return tagm(o, v);
pushdown(o);
int mid = (l + r) >> 1;
if (L <= mid)
Mul(o << 1, l, mid, L, R, v);
if (R > mid)
Mul(o << 1 | 1, mid + 1, r, L, R, v);
pushup(o);
}
int Query() { return s[1]; }
void dfs(int o, int l, int r) {
if (l == r) {
cout << s[o] << " ";
return;
}
pushdown(o);
int mid = (l + r) >> 1;
dfs(o << 1, l, mid);
dfs(o << 1 | 1, mid + 1, r);
}
} seg;
int d[1000005], dcnt;
signed main() {
// int ttt = clock();
freopen("candy.in", "r", stdin);
freopen("candy.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i++) a[i] = read(), d[i] = a[i], a[i + n] = a[i];
sort(d + 1, d + n + 1);
dcnt = unique(d + 1, d + n + 1) - d - 1;
for (int i = 1; i <= dcnt; i++) v[i] = d[i] - d[i - 1];
seg.Build(1, 1, dcnt);
for (int i = 1; i <= n; i++) {
if (!mp || a[i] < a[mp])
mp = i;
}
a[mp] = lower_bound(d + 1, d + dcnt + 1, a[mp]) - d;
seg.Add(1, 1, dcnt, 1, a[mp], 1);
int ans = 0;
for (int i = mp + 1, s = seg.Query(); i < mp + n; i++) {
// seg.dfs(1, 1, dcnt);
// cout << "\n";
a[i] = lower_bound(d + 1, d + dcnt + 1, a[i]) - d;
if (a[i] < dcnt)
seg.Mul(1, 1, dcnt, a[i] + 1, dcnt, 0);
seg.Mul(1, 1, dcnt, 1, dcnt, P - 1);
seg.Add(1, 1, dcnt, 1, a[i], s);
s = seg.Query();
if ((mp + n - i) & 1)
ans += s;
else
ans -= s;
ans = (ans + P) % P;
// seg.dfs(1, 1, dcnt);
// cout << "\n";
}
cout << ans << "\n";
// cerr << (1.0 * clock() - ttt) / CLOCKS_PER_SEC << "\n";
return 0;
}
T2
光明
首先 \(f(u, i)\) 即为 \(u\) 子树内距离 \(u\) 深度为 \(i\) 的点个数。然后注意到对于每一个深度,它对上面的 \(u\) 的贡献是 \(u\) 深度越浅就越大的。再观察一下就会发现这个深度对于其上面的 \(u\) 的本质不同贡献只有 \(O(该深度点个数)\) 种,也就是这些点虚树上每一条边内部的点。于是总共来说本质不同的 \(f(u, i)\) 就是 \(O(n)\) 级别,可以直接全搞出来排序做。
代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
char buf[1<<21],*p1,*p2,ch;
long long read(){
long long ret=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')ret=ret*10+c-'0',c=getchar();
return ret;
}
int head[3000005], nxt[3000005], to[3000005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int fa[3000005], dfn[3000005], _dfn[3000005], dep[3000005], ncnt;
int sz[3000005], son[3000005], top[3000005];
vector<int> vec[3000005];
void dfs1(int x, int d) {
sz[x] = 1;
dep[x] = d;
_dfn[dfn[x] = ++ncnt] = x;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if (v != fa[i]) {
dfs1(v, d + 1);
sz[x] += sz[v];
if (sz[v] > sz[son[x]])
son[x] = v;
}
}
}
void dfs2(int x, int t) {
top[x] = t;
if (son[x])
dfs2(son[x], t);
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if (v != son[x] && v != fa[x])
dfs2(v, v);
}
}
int LCA(int x, int y) {
while (top[x] ^ top[y]) (dep[top[x]] < dep[top[y]]) ? (y = fa[top[y]]) : (x = fa[top[x]]);
return (dep[x] < dep[y] ? x : y);
}
int n;
long long K;
int vsz[3000005];
struct node {
int v, cnt;
};
vector<node> ans;
int stk[3000005], ssz;
void work(vector<int>& key) {
int kcnt = key.size();
stk[ssz = 1] = 1;
vsz[1] = (key[0] == 1);
for (int i = (key[0] == 1); i < kcnt; i++) {
int t = LCA(stk[ssz], key[i]);
vsz[key[i]] = 1;
if (stk[ssz] != t) {
while (ssz > 1 && dfn[t] < dfn[stk[ssz - 1]]) {
vsz[stk[ssz - 1]] += vsz[stk[ssz]];
ans.emplace_back((node) { vsz[stk[ssz]], dep[stk[ssz]] - dep[stk[ssz - 1]] });
--ssz;
}
if (t != stk[ssz - 1]) {
vsz[t] = 0;
vsz[t] += vsz[stk[ssz]];
ans.emplace_back((node) { vsz[stk[ssz]], dep[stk[ssz]] - dep[t] });
stk[ssz] = t;
} else {
vsz[t] += vsz[stk[ssz]];
ans.emplace_back((node) { vsz[stk[ssz]], dep[stk[ssz]] - dep[t] });
--ssz;
}
}
stk[++ssz] = key[i];
}
for (int i = ssz; i > 1; i--) vsz[stk[i - 1]] += vsz[stk[i]], ans.emplace_back((node) { vsz[stk[i]], dep[stk[i]] - dep[stk[i - 1]] });
ans.emplace_back((node) { vsz[1], 1 });
}
signed main() {
freopen("light.in", "r", stdin);
freopen("light.out", "w", stdout);
n = read(), K = read();
for (int i = 2; i <= n; i++) add(fa[i] = read(), i);
dfs1(1, 1);
dfs2(1, 1);
for (int i = 1; i <= n; i++) vec[dep[_dfn[i]]].emplace_back(_dfn[i]);
for (int i = 1; i <= n; i++) {
if (vec[i].empty())
break;
work(vec[i]);
}
sort(ans.begin(), ans.end(), [](node a, node b) { return a.v > b.v; });
long long aans = 0;
for (auto v : ans) {
if (K <= v.cnt) {
aans += 1ll * v.v * K;
break;
}
K -= v.cnt;
aans += 1ll * v.v * v.cnt;
}
cout << aans << "\n";
return 0;
}
T3
游戏
按 \(k\) 大小分开做。\(k\) 小矩乘,\(k\) 大容斥。后面忘了。
线段树操作和标记少一点,维护简单且跑得快。
不 #define int long long 的时候,快读函数的返回值也要开 ll。

浙公网安备 33010602011771号