数据结构
自古不会数据结构,只会瞎写。/kk
CF1039D You Are Given a Tree
题目大意
给定一棵树,对于 k∈[1,n]
题解
首先假设只有一个给定的 k
直接做需要奇怪的卡常科技,不知道为什么整体二分会快这么多。。而且好写。。不会分析复杂度。。
code
#include <bits/stdc++.h>
const int N = 1e5 + 10;
int read(void){
int f = 1, x = 0; char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
while(isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); }
return f * x;
}
int n, ans[N], f[N];
int head[N], nxt[N << 1], to[N << 1], cnt;
void add(int u, int v)
{ nxt[++cnt] = head[u], to[cnt] = v, head[u] = cnt; }
int dfn[N], num[N], fa[N], ind;
void dfs(int u, int f){
dfn[u] = ++ind, fa[num[ind] = u] = f;
for(int i = head[u]; i; i = nxt[i]){
int v = to[i];
if(v ^ f) dfs(v, u);
}
}
int calc(int x){
int res = 0;
for(int i = n; i; --i){
int u = num[i], a = 0, b = 0;
for(int i = head[u]; i; i = nxt[i]){
int v = to[i];
if(v == fa[u]) continue;
if(f[v] >= a) b = a, a = f[v];
else if(f[v] > b) b = f[v];
}
if(a + b + 1 >= x) ++res, f[u] = 0;
else f[u] = a + 1;
}
return res;
}
void solve(int l, int r, int L, int R){
if(l > r || L > R) return;
if(l == r){
for(int i = L; i <= R; ++i) ans[i] = l;
return;
}
int mid = (L + R) >> 1; ans[mid] = calc(mid);
solve(ans[mid], r, L, mid - 1); solve(l, ans[mid], mid + 1, R);
}
signed main(void){
n = read();
for(int i = 1, x, y; i < n; ++i){
x = read(), y = read();
add(x, y), add(y, x);
}
dfs(1, 0); ans[1] = n;
solve(0, n / 2, 2, n);
for(int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
return 0;
}
CF983E NN country
题目大意
有一棵树,树上有若干条路径,每条路径 x,y
题解
首先不难求出对于每个点经过一条路径所能到达的最靠上的点在哪里,于是可以考虑对其进行倍增,每次询问 (u,v)
没啥细节就不放代码了。。。
CF1422F Boring Queries
题目大意
给定一个长度为 n
题解
考虑 lcm
code
#include <bits/stdc++.h>
#define fi first
#define se second
const int N = 2e5 + 10, mod = 1e9 + 7;
int read(void) {
int f = 1, x = 0; char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
while(isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); }
return f * x;
}
using std::pair;
using std::make_pair;
int inv[N], pri[N], cnt;
bool vis[N];
std::unordered_map<int, int> mp[N];
void pre(void) {
inv[0] = inv[1] = 1;
for(int i = 2; i < N; ++i) {
if(!vis[i]) pri[++cnt] = i;
for(int j = 1; j <= cnt && pri[j] * i < N; ++j)
{ vis[i * pri[j]] = 1; if(!(i % pri[j])) break; }
inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
}
for(int i = 1; i <= cnt; ++i) for(int j = 1; j * pri[i] < N; ++j)
mp[j * pri[i]][i] = j % pri[i] ? pri[i] : mp[j][i] * pri[i];
}
struct range { int l, r, w; };
std::vector<range> pos[N];
int n, q, a[N], rt[N], tot;
struct Tree { int ls, rs, mul; } t[N << 7];
void add(int& p, int lst, int l, int r, int w, int L = 1, int R = n) {
t[p = ++tot] = t[lst]; int mid = (L + R) >> 1;
if(l <= L && r >= R) { return void(t[p].mul = 1ll * t[p].mul * w % mod); }
if(l <= mid) add(t[p].ls, t[lst].ls, l, r, w, L, mid);
if(r > mid) add(t[p].rs, t[lst].rs, l, r, w, mid + 1, R);
}
int que(int p, int x, int L = 1, int R = n) {
if(L == R) return t[p].mul; int mid = (L + R) >> 1;
if(x <= mid) return 1ll * t[p].mul * que(t[p].ls, x, L, mid) % mod;
else return 1ll * t[p].mul * que(t[p].rs, x, mid + 1, R) % mod;
}
signed main(void) {
n = read(), t[0].mul = 1, pre();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= n; ++i) {
rt[i] = rt[i - 1];
for(pair<int, int> x : mp[a[i]]) {
while(!pos[x.fi].empty() && pos[x.fi].back().w <= x.se) {
range ul = pos[x.fi].back(); pos[x.fi].pop_back();
add(rt[i], rt[i], ul.l, ul.r, inv[ul.w]);
}
int p = pos[x.fi].empty() ? 1 : pos[x.fi].back().r + 1;
pos[x.fi].push_back( { p, i, x.se } );
add(rt[i], rt[i], p, i, x.se);
}
} q = read();
for(int i = 1, l, r, lst = 0; i <= q; ++i) {
l = (read() + lst) % n + 1, r = (read() + lst) % n + 1;
if(l > r) l ^= r, r ^= l, l ^= r;
printf("%d\n", lst = que(rt[r], l));
}
return 0;
}
CF503E Pastoral Oddities
题目大意
给定一张 n 个点 m 条边的图,初始时没有边,每次往里加一条边,边有边权,求是否存在一个边集满足每个点的度数均为奇数,若存在,输出边集中最大边权的最小值。
题解
乍一看像是神仙题,果然是神仙题。。
有一个很强的结论,每个点的度数为奇数的充要条件为所有联通块的大小均为偶数。
必要性证明:考虑反证法,大小为奇数的连通块内所有点的度数为奇数,则一定有连通块内度数和为奇数,而一个无向图连通块度数和一定为偶数,相悖。
充分性证明:考虑有一个偶数大小的连通块,我们一定可以通过以下方法构造出一种合法解,从叶子开始向上走,一个点若有奇数个儿子,就删掉它和父亲之间的边,最终除了根节点和被断开的节点,所有点都有偶数个儿子。
这样,我们可以考虑维护这张图的最小生成森林,维护每棵树的大小,同时在 link 和 cut 时维护奇连通块的数量,这里可以用 LCT 维护子树信息实现。考虑每次新加入一条边奇连通块数量肯定不会变多,因此每一条新边能加入就加入,此时有一些边可能对奇联通块数没有影响,删除最大的对奇连通块数没有影响的边,可以用一个堆来维护森林中的所有边,不能删除的最大的边便是答案。
code
//How I wish, how I wish you were here.
#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
const int N = 5e5 + 10;
int read(void) {
int f = 1, x = 0; char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
while(isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); }
return f * x;
}
using std::pair; using std::make_pair;
int n, m, u[N], v[N], w[N];
bool Cut[N];
std::priority_queue<pair<int, int> > q;
struct Link_Cut_Tree {
int fa[N], siz[N], son[N][2], inv[N], stk[N], top, cnt;
std::pair<int, int> mx[N], a[N];
bool lz[N];
bool right(int p) { return son[fa[p]][1] == p; }
bool nroot(int p) { return son[fa[p]][0] == p || son[fa[p]][1] == p; }
void update(int p) {
mx[p] = std::max(std::max(mx[son[p][0]], mx[son[p][1]]), a[p]);
siz[p] = inv[p] + siz[son[p][0]] + siz[son[p][1]] + (p <= n);
}
void pushrev(int p) { lz[p] ^= 1, std::swap(son[p][0], son[p][1]); }
void pushdown(int p) {
if(!lz[p]) return; lz[p] = 0;
if(son[p][0]) pushrev(son[p][0]);
if(son[p][1]) pushrev(son[p][1]);
}
void rotate(int p) {
int f1 = fa[p], f2 = fa[f1], id = right(p), w = son[p][id ^ 1];
if(nroot(f1)) son[f2][right(f1)] = p; son[p][id ^ 1] = f1, son[f1][id] = w;
if(w) fa[w] = f1; fa[f1] = p, fa[p] = f2; update(f1);
}
void splay(int p) {
int u = p; stk[top = 1] = u;
while(nroot(u)) stk[++top] = u = fa[u];
while(top) pushdown(stk[top--]);
while(nroot(p)) {
if(nroot(fa[p]))
rotate(right(p) == right(fa[p]) ? fa[p] : p);
rotate(p);
} update(p);
}
void access(int p) {
for(int x = 0; p; p = fa[x = p]) {
splay(p), inv[p] += siz[son[p][1]];
inv[p] -= siz[son[p][1] = x], update(p);
}
}
void makert(int p)
{ access(p), splay(p), pushrev(p); }
int findrt(int p) {
access(p), splay(p), pushdown(p);
while(son[p][0]) pushdown(p = son[p][0]);
return splay(p), p;
}
void link(int u, int v) {
makert(u), access(v), splay(v);
cnt -= siz[u] & 1, cnt -= siz[v] & 1;
fa[u] = v, inv[v] += siz[u], update(v);
cnt += siz[v] & 1;
}
void cut(int u, int v) {
makert(u), access(v), splay(v), cnt -= siz[v] & 1;
son[v][0] = fa[u] = 0, update(v);
cnt += siz[u] & 1, cnt += siz[v] & 1;
}
int add(int id) {
int u = ::u[id], v = ::v[id], w = ::w[id];
bool flag = 1;
if(findrt(u) == findrt(v)) {
makert(u), access(v), splay(v);
int ul = mx[v].se;
if(w < mx[v].fi)
cut(ul, ::u[ul - n]), cut(ul, ::v[ul - n]), Cut[ul - n] = 1;
else flag = 0;
}
if(flag) {
a[id + n] = mx[id + n] = make_pair(w, id + n);
link(id + n, u), link(id + n, v), q.push(make_pair(w, id));
}
if(cnt) return -1;
while(!q.empty()) {
int id = q.top().se; q.pop();
if(Cut[id]) continue;
cut(id + n, ::u[id]), cut(id + n, ::v[id]);
if(cnt) {
link(id + n, ::u[id]), link(id + n, ::v[id]);
q.push(make_pair(::w[id], id)); return ::w[id];
}
}
}
} t;
signed main(void) {
t.cnt = n = read(), m = read();
for(int i = 1; i <= n; ++i) t.siz[i] = 1;
for(int i = 1; i <= m; ++i) {
u[i] = read(), v[i] = read(), w[i] = read();
printf("%d\n", t.add(i));
}
return 0;
}
605菜市场
改编自模拟赛的水题,太水所以没有题解,欢迎大家爆切。
code
#include <bits/stdc++.h>
#define int long long
const int N = 1e5 + 10;
int read(void) {
int f = 1, x = 0; char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
while(isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); }
return f * x;
}
int n, q, g, r, op, mod;
int tot, rt[N], d[N], f[N], stp[N][20];
struct TRE { int ls, rs, mn; } t[N << 7];
void cover(int &p, int lst, int l, int r, int w, int L = 0, int R = mod - 1) {
t[p = ++tot] = t[lst]; if(!lst) t[p].mn = n;
if(l <= L && r >= R) return void(t[p].mn = w);
int mid = (L + R) >> 1;
if(l <= mid) cover(t[p].ls, t[lst].ls, l, r, w, L, mid);
if(r > mid) cover(t[p].rs, t[lst].rs, l, r, w, mid + 1, R);
}
int que(int p, int x, int L = 0, int R = mod - 1) {
if(!p) return n;
if(L == R) return t[p].mn; int mid = (L + R) >> 1;
if(x <= mid) return std::min(t[p].mn, que(t[p].ls, x, L, mid));
else return std::min(t[p].mn, que(t[p].rs, x, mid + 1, R));
}
signed main(void) {
n = read(), q = read(), g = read(), r = read(), op = read(), mod = r + g;
for(int i = 1; i <= n; ++i) d[i] = d[i - 1] + read();
for(int i = n - 1, p, L, R; i; --i) {
stp[i][0] = p = que(rt[i + 1], d[i] % mod);
f[i] = f[p] + d[p] - d[i];
if(p ^ n) f[i] += mod - (d[p] - d[i]) % mod;
for(int j = 1; j < 20; ++j)
stp[i][j] = stp[stp[i][j - 1]][j - 1];
R = (d[i] - g + mod) % mod, L = (d[i] - g - r + 1 + mod) % mod;
if(L <= R) cover(rt[i], rt[i + 1], L, R, i);
else cover(rt[i], rt[i + 1], L, mod - 1, i), cover(rt[i], rt[i], 0, R, i);
}
for(int i = 1, t, l, r, res = 0, p; i <= q; ++i) {
t = read(), l = (read() ^ (op * res)) % (n + 1), r = (read() ^ (op * res)) % (n + 1);
if(l > r) l ^= r, r ^= l, l ^= r;
p = que(rt[l + 1], (mod + d[l] % mod - t % mod) % mod);
if(p >= r) { printf("%lld\n", res = d[r] - d[l] + t); continue; }
res = d[p] - d[l] + t + mod - (d[p] - d[l] + t) % mod + f[p];
for(int j = 19; ~j; --j) if(stp[p][j] && stp[p][j] < r) p = stp[p][j];
res += d[r] - d[p] - f[p]; printf("%lld\n", res);
}
return 0;
}
__EOF__