T1:CF19E Fairy
首先暴力的线段树分治可以拿
70
p
t
s
70pts
70pts
有一个遗憾就是考场想了一半正解然后发现线段树分治可以拿不错的分数就苟且偷生了,看来以后做题有时间的话还是要追求卓越
考场上的思路就是先
d
f
s
dfs
dfs出一棵树,然后发现不能有奇环
然后我发现合法的边必须出现在所有奇环中,然后傻逼地打了个树剖 (差分就可以完事)
然后开始对拍,发现这么做总比
s
t
d
std
std跑出来多,然后我发现一个环可能还含有两条非树边
c
n
m
cnm
cnm 这可做吗?然后就滚去打了个线段树分治
考虑多条非树边对答案的影响:
多条太智障了,先考虑两条
1.它们不相交,可以去死了
2.它们相交
讨论:两条奇环:还有搞,一奇一偶:删去这条边后,这两个环会连成一个奇环
于是乎合法的边的条件是:
1.被所有奇环覆盖
2.不被任何一个偶环覆盖
然后差分即可
还有一个性质就是无向图
d
f
s
dfs
dfs 出来的一棵树只会有反祖边,不会有横插边
因为如果插到了两棵树上从第一棵就会直接到第二棵了
#include<bits/stdc++.h>
#define N 2000005
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
return cnt * f;
}
struct Edge{ int u, v, od, ev, flg; } e[N];
int first[N], nxt[N << 1], to[N << 1], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int n, m, dep[N], cnt, ev[N], od[N]; bool vis[N];
void dfs(int u){
vis[u] = 1;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(vis[t]) continue;
dep[t] = dep[u] + 1; e[(i + 1) >> 1].flg = 1; dfs(t);
}
}
void dfs2(int u){
vis[u] = 1;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(!e[(i + 1) >> 1].flg || vis[t]) continue;
dfs2(t); ev[u] += ev[t]; od[u] += od[t];
e[(i + 1) >> 1].od = od[t]; e[(i + 1) >> 1].ev = ev[t];
}
}
vector<int> ans;
int main(){
n = read(), m = read();
for(int i = 1; i <= m; i++){
int x = read(), y = read();
add(x, y); add(y, x);
e[i] = (Edge){x, y, 0, 0, 0};
}
for(int i = 1; i <= n; i++) if(!vis[i]) dfs(i);
for(int i = 1; i <= m; i++){
if(e[i].flg) continue;
int u = e[i].u, v = e[i].v;
if(dep[u] > dep[v]) swap(u, v);
if((dep[v] - dep[u]) & 1) ev[u]--, ev[v]++;
else od[u]--, od[v]++, cnt++, e[i].od = 1;
}
if(cnt == 0){
cout << m << '\n';
for(int i = 1; i <= m; i++) cout << i << " ";
return 0;
}
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++) if(!vis[i]) dfs2(i);
for(int i = 1; i <= m; i++){
if(e[i].flg){
if(e[i].od == cnt && e[i].ev == 0) ans.push_back(i);
} else if(cnt == 1 && e[i].od) ans.push_back(i);
}
cout << ans.size() << '\n';
for(int i = 0; i < ans.size(); i++) cout << ans[i] << " ";
return 0;
}
T2:BZOJ 3682
给你一个字符串
S
S
S,初始长度为
l
e
n
len
len,还有一个
n
n
n 个元素的序列
P
P
P
接下来
m
m
m 个操作,有三种类型,分别是:
1 在字符串前面加入一个字符
2. 修改
P
P
P 中一个元素的值
3. 询问对于所有
i
∈
[
l
,
r
]
i∈[l,r]
i∈[l,r],使得
S
[
L
−
P
[
i
]
+
1..
L
]
S[L-P[i]+1..L]
S[L−P[i]+1..L]字典序最小的
i
i
i
强制在线
发现需要在线获得一个数的排名
法 1:
考虑直接用线段树维护最小的 i,难点就在
p
u
s
h
u
p
pushup
pushup 了
发现每次
p
u
s
h
u
p
pushup
pushup 都要比两个的大小,于是二分 + hash 就有
10
p
t
s
10pts
10pts的
O
(
n
∗
l
o
g
2
n
)
O(n*log^2n)
O(n∗log2n)算法
法 2:
在前面加,好像就是个后缀平衡树的板子
关于后缀平衡树:
支持,往前插,从前面删,得到某个后缀的
r
k
rk
rk,
S
A
SA
SA,比较后缀大小
怎么维护呢,简单,跟平衡树一样,插进去的时候跟当前比,比用二分 + hash,于是我们又回到了
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n) 的好算法
考虑到当前点之前的后缀都被插过了,于是比的时候先比第一个字符
如果一样就比后面,而后面的已经知道
然后给每个点分配一个很大很大的权值,比大小就可以
O
(
1
)
O(1)
O(1) 了
于是就可以
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 做了
平衡树维护的时候如果
r
o
t
a
t
e
rotate
rotate 了就暴力重建子树分配的权值,期望次数不会很多
#include<bits/stdc++.h>
#define N 1000050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int Rnd(){ return rand() | (rand() << 15);}
int n, m, len, pos[N], ans, rt;
char S[N];
typedef long long ll;
int ch[N][2], rnd[N], val[N], node; ll key[N];
bool cmp(int x, int y){ return val[x] < val[y] || val[x] == val[y] && key[x-1] < key[y-1];}
void pia(int x, ll l, ll r){
if(!x) return; ll mid = (l+r) >> 1;
key[x] = mid; pia(ch[x][0], l, mid - 1); pia(ch[x][1], mid + 1, r);
}
void rot(int x, int &y, ll l, ll r){
int k = ch[y][1] == x; ch[y][k] = ch[x][k^1];
ch[x][k^1] = y; y = x; pia(y, l, r);
}
void ins(int &x, int p, ll l, ll r){
if(!x){ x = node; key[node] = (l + r) >> 1; return; }
ll mid = (l+r) >> 1;
if(cmp(p, x)){
ins(ch[x][0], p, l, mid - 1);
if(rnd[x] > rnd[ch[x][0]]) rot(ch[x][0], x, l, r);
}
else{
ins(ch[x][1], p, mid + 1, r);
if(rnd[x] > rnd[ch[x][1]]) rot(ch[x][1], x, l, r);
}
}
struct Segmentree{
int v[N << 2];
int merge(int a, int b){ return key[pos[a]] <= key[pos[b]] ? a : b;}
void pushup(int x){ v[x] = merge(v[x<<1], v[x<<1|1]); }
void build(int x, int l, int r){
if(l == r){ v[x] = l; return; }
int mid = (l+r) >> 1;
build(x<<1, l, mid); build(x<<1|1, mid+1, r);
pushup(x);
}
void modify(int x, int l, int r, int p, int val){
if(l == r){ pos[p] = val; return;} int mid = (l+r) >> 1;
if(p <= mid) modify(x<<1, l, mid, p, val);
else modify(x<<1|1, mid+1, r, p, val);
pushup(x);
}
int query(int x, int l, int r, int L, int R){
if(L<=l && r<=R) return v[x]; int mid = (l+r) >> 1;
if(L>mid) return query(x<<1|1, mid+1, r, L, R);
else if(R<=mid) return query(x<<1, l, mid, L, R);
else return merge(query(x<<1, l, mid, L, R), query(x<<1|1, mid+1, r, L, R));
}
}Seg;
int main(){
n = read(), m = read(); len = read();
scanf("%s", S + 1);
for(int i = 1; i <= len; i++){
val[++node] = S[len - i + 1] - 'a' + 1;
rnd[node] = Rnd();
ins(rt, i, 1, 1ll << 61);
}
for(int i = 1; i <= n; i++) pos[i] = read();
Seg.build(1, 1, n);
while(m--){
char op[3]; scanf("%s", op);
if(op[0] == 'I'){
int x = (read() ^ ans) + 1;
val[++node] = x; rnd[node] = Rnd();
ins(rt, node, 1, 1ll << 61);
}
if(op[0] == 'C'){ int x = read(), y = read(); Seg.modify(1, 1, n, x, y); }
if(op[0] == 'Q'){ int l = read(), r = read(); ans = Seg.query(1, 1, n, l, r); cout << ans << '\n';}
} return 0;
}
CF768G The Winds of Winter
删除一个点后肯定把最大的分配给最小的
于是二分一个
m
i
d
mid
mid,最少分
m
a
x
−
m
i
d
max-mid
max−mid,最多分
m
i
d
−
m
i
n
mid-min
mid−min
于是就成了查询子树有没有在
[
m
a
x
−
m
i
d
,
m
i
d
−
m
i
n
]
[max-mid,mid-min]
[max−mid,mid−min] 区间的
s
i
z
siz
siz
直接主席树
等等,最大在父亲怎么办,发现有影响的只有这一条链
链上所有的点的
s
i
z
siz
siz 会减去
s
i
z
n
o
w
siz_{now}
siznow
于是可以维护一个链的主席树,先减去
[
m
a
x
−
m
i
d
,
m
i
d
−
m
i
n
]
[max-mid,mid-min]
[max−mid,mid−min]的贡献,再加上
[
m
a
x
−
m
i
d
+
s
i
z
n
o
w
,
m
i
d
−
m
i
n
+
s
i
z
n
o
w
]
[max-mid+siz_{now},mid-min+siz_{now}]
[max−mid+siznow,mid−min+siznow] 的贡献就可以了
#include<bits/stdc++.h>
#define N 100050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int n, root;
int first[N], nxt[N], to[N], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int siz[N], st[N], ed[N], sign, pos[N], ans[N];
struct Presidentree{
struct Node{ int ls, rs, val; } t[N * 20];
int rt[N], node;
void build(int &x, int l, int r){
x = ++node; if(l == r) return; int mid = (l+r) >> 1;
build(t[x].ls, l, mid); build(t[x].rs, mid+1, r);
}
void ins(int &x, int las, int l, int r, int p){
x = ++node; t[x] = t[las]; ++t[x].val; if(l == r) return; int mid = (l+r) >> 1;
if(p <= mid) ins(t[x].ls, t[las].ls, l, mid, p); else ins(t[x].rs, t[las].rs, mid+1, r, p);
}
int query(int a, int b, int l, int r, int L, int R){
if(!a) return 0;
if(L<=l && r<=R) return t[a].val - t[b].val;
int mid = (l+r) >> 1, ans = 0;
if(L<=mid) ans += query(t[a].ls, t[b].ls, l, mid, L, R);
if(R>mid) ans += query(t[a].rs, t[b].rs, mid+1, r, L, R);
return ans;
}
}T[2];
void dfs(int u){
siz[u] = 1; st[u] = ++sign; pos[sign] = u;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; dfs(t); siz[u] += siz[t];
} ed[u] = sign;
}
void get(int u, int fa){
T[1].ins(T[1].rt[u], T[1].rt[fa], 1, n, siz[u]);
for(int i = first[u]; i; i = nxt[i]) get(to[i], u);
}
int id[N];
void calc(int u, int fa){
vector<int> tp;
for(int i = first[u]; i; i = nxt[i]) tp.push_back(siz[to[i]]), id[siz[to[i]]] = to[i];
if(n - siz[u]) tp.push_back(n - siz[u]), id[n - siz[u]] = fa;
sort(tp.begin(), tp.end());
int num = tp.size();
if(num == 1){ ans[u] = tp[0]; }
if(tp[num - 1] == tp[num - 2]){ ans[u] = tp[num - 1];}
if(!ans[u]){
int l = max(tp[num - 2], (tp[0] + tp[num - 1] + 1) / 2), r = tp[num - 1];
int now = id[tp[num - 1]];
while(l < r){
int mid = (l+r) >> 1;
if(now == fa){
int sum = T[0].query(T[0].rt[ed[root]], T[0].rt[st[root]-1], 1, n, tp[num - 1] - mid, mid - tp[0]);
sum -= T[0].query(T[0].rt[ed[u]], T[0].rt[st[u]-1], 1, n, tp[num - 1] - mid, mid - tp[0]);
sum -= T[1].query(T[1].rt[fa], T[1].rt[0], 1, n, tp[num - 1] - mid, mid - tp[0]);
sum += T[1].query(T[1].rt[fa], T[1].rt[0], 1, n, tp[num - 1] - mid + siz[u], mid - tp[0] + siz[u]);
if(sum > 0) r = mid;
else l = mid + 1;
}
else{
if(T[0].query(T[0].rt[ed[now]], T[0].rt[st[now]-1], 1, n, tp[num - 1] - mid, mid - tp[0])) r = mid;
else l = mid + 1;
}
} ans[u] = l;
}
for(int i = first[u]; i; i = nxt[i]) calc(to[i], u);
}
int main(){
freopen("winter.in","r",stdin);
freopen("winter.out","w",stdout);
n = read();
for(int i = 1; i <= n; i++){
int x = read(), y = read(); if(!x) root = y;
else add(x, y);
}
T[1].build(T[1].rt[0], 1, n);
dfs(root); get(root, 0);
T[0].build(T[0].rt[0], 1, n);
for(int i = 1; i <= n; i++) T[0].ins(T[0].rt[i], T[0].rt[i-1], 1, n, siz[pos[i]]);
calc(root, 0);
for(int i = 1; i <= n; i++) cout << ans[i] << '\n';
return 0;
}