整体二分一类题目中的高端题
如果不是有向图,这个题也就是个紫题水平。直接倒序加边,每个点维护一棵权值线段树,再用并查集判联通块即可……
但是,它是有向图……
这意味着我们根本不知道什么时候他会构成连通块。
考虑每个有向边,对一条边连接的两个点来说,肯定有一个时刻它们不再在同一个连通块里,所以单个边考虑的话,只需要对序列二分判强连通就知道从什么时候开始它们不在同一个连通块里。
然后发现了单个可以二分之后,就可以联想到对序列整体进行二分来求出每一个边的分离时间。
之后求出每个分离时间之后,再按正常套路倒序加边,并查集维护连通块,线段树合并维护强连通第k大即可
(没想到我人生中第一道线段树合并是这个题……
detail:
这个题的细节巨多,能恶心死你
尤其是整体二分那块真的是看到就恶心
还有,权值线段树开成值域范围,不要像我一样开成n的大小……
线段树合并用指针真能把人气死 , 一个个特判看着就烦
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 1ll<<30
#define pb push_back
#define pii pair<int ,int >
const int p=2e6+5;
template<typename _T>
inline void read(_T &x)
{
x=0;char s=getchar();int f=1;
while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
x*=f;
}
struct Edge{
int u,v,tim;
}e[p];
map<pii,int> mapp;
int a[p],b[p*4] , bili;
int n,m,q;
struct node{
int opt , a, b , tim;
}c[p*4];
struct query{
int opt , a , b;
}qa[p];
struct segment{
struct tree{
int l,r;
tree *ls , *rs;
int cnt , sum;
inline void pushup(){cnt = ls->cnt + rs->cnt,sum = ls->sum + rs->sum;}
inline int query(int k)
{
if(!this) return 0;
if(cnt < k) return sum;
if(l == r) return k*b[l];
if(rs && k<=rs->cnt) return rs->query(k);
else return (rs?rs->sum:0 )+ ls->query(k - (rs?rs->cnt:0));
}
}mem[p * 4],*pool = mem;
tree *rot[p];
inline tree *New(int L,int R){++pool;pool->l = L;pool->r = R;return pool;}
inline void update(tree *u,int pos,int add)
{
u->cnt+=add;
u->sum += add*b[pos];
if(u->l == u->r) return;
int mid = u->l + u->r >> 1;
int L = u->l , R = u->r;
if(pos <= mid) update((u->ls)?u->ls:(u->ls = New(L,mid)) , pos , add);
else update(u->rs?u->rs:(u->rs = New(mid+1,R)) , pos , add);
}
inline void Merge(tree *a, tree *b)
{
if(!b) return;
else if(!a->sum&&!a->cnt){*a = *b;return;}
if(a->l == a->r) {a->cnt += b->cnt , a->sum += b->sum ; return;}
else
{
int L = a->l , R = a->r , mid = L+R>>1;
Merge(a->ls?a->ls:a->ls = New(L,mid),b->ls);
Merge(a->rs?a->rs:a->rs = New(mid+1,R) , b->rs);
if(a->ls&&a->rs) a->pushup();
else{
if(a->ls) a->cnt = a->ls->cnt , a->sum = a->ls->sum;
else a->cnt = a->rs->cnt, a->sum = a->rs->sum;
}
}
}
inline void merge(int x,int y) {Merge(rot[x] , rot[y]);}
}segT;
struct bcj{
Edge st[p];
int top;
int fa[p] , dep[p];
inline void Init(){for(int i=1;i<=n;i++) fa[i] = i , dep[i] = 1;}
inline int f(int x){while(x!=fa[x])x = fa[x];return fa[x];}
inline void unite(int x,int y)
{
x = f(x) , y = f(y);
if(x == y) return;
else
{
if(dep[x] < dep[y]) swap(x , y);
fa[y] = x;
st[++top] = {x , y ,dep[x] == dep[y]};
dep[x] += dep[y]==dep[x];
}
}
inline void Del(int lim)
{
while(top>lim)
{
int u = st[top].u,v = st[top].v;
int add = st[top].tim;
fa[v] = v;
dep[u] -= add;
top--;
}
}
inline void Merge(int x,int y)
{
x = f(x) , y = f(y);
if(x == y) return;
else
{
if(dep[x] < dep[y]) swap(x , y);
fa[y] = x;
dep[x] += dep[y]==dep[x];
segT.merge(x , y);
}
}
}s,s1;
struct shp{
int dfn[p] , low[p];
int head[p],nxt[p],ver[p],tit;
int top , st[p] , lim,vis[p];
inline void add(int x,int y)
{
ver[++tit] = y;
nxt[tit] = head[x];
head[x] = tit;
}
inline void tarjan(int x)
{
dfn[x] = low[x] = ++lim;
vis[x] = 1;st[++top] = x;
for(int i=head[x] ;i;i =nxt[i])
{
int v = ver[i];
if(!dfn[v]) tarjan(v) , low[x] = min(low[x] , low[v]);
else if(vis[v]) low[x] = min(dfn[v] , low[x]);
}
if(dfn[x] == low[x])
{
int now = st[top--];
vis[now] = 0;
while(st[top + 1]!=x)
{
vis[st[top]] = 0;
s.unite(now , st[top--]);
}
}
}
inline void solve(vector<int> x){for(auto i:x)if(!dfn[i]) tarjan(i);}
inline void clear(vector<int> x){for(auto i:x) head[i] = low[i] = dfn[i] = 0;lim = tit = 0;}
}Tarjan;
Edge lef[p],rig[p];
int Tl,Tr;
vector<int> vec;
inline void solve(int vl,int vr,int l,int r)
{
if(l>r) return;
if(vl == vr)
{
for(int i=l;i<=r;i++) e[i].tim = vl;
return;
}
int mid = vl + vr >>1;
vec.clear();
int lim = s.top;
for(int i=l;i<=r;i++)
{
int u = e[i].u , v = e[i].v;
int r1 = s.f(u) , r2 = s.f(v);
int tim = e[i].tim;
if(tim <= mid) Tarjan.add(r1,r2),vec.pb(r1) , vec.pb(r2);
}
Tarjan.solve(vec);
Tl = Tr = 0;
for(int i=l;i<=r;i++)
{
int u = e[i].u , v = e[i].v;
int tim = e[i].tim;
int r1 = s.f(u) , r2 = s.f(v);
if(r1 != r2 || tim > mid) rig[++Tr] = e[i];
else lef[++Tl] = e[i];
}
for(int i=l;i<=l+Tl - 1;i++)
{
e[i] = lef[i - l + 1];
}
for(int i=l+Tl;i<=r;i++)
{
e[i] = rig[i - (l + Tl - 1)];
}
Tarjan.clear(vec);int cop = Tl;
solve(mid+1,vr,Tl + l , r);
s.Del(lim);
solve(vl,mid,l,l+cop-1);
}
int ans[p];
bitset<p> bit;
int *en ;
inline int True(int x)
{
return lower_bound(b+1 ,en , x) - b;
}
signed main()
{
read(n);
read(m);
read(q);
for(int i=1;i<=n;i++) read(a[i]) , b[++bili] = a[i];
for(int i=1;i<=m;i++)
{
read(e[i].u);
read(e[i].v);
e[i].tim = 0;
mapp[{e[i].u ,e[i].v}] = i;
}
for(int i=1;i<=q;i++)
{
read(qa[i].opt);read(qa[i].a);read(qa[i].b);
if(qa[i].opt == 1) e[mapp[{qa[i].a,qa[i].b}]].tim = q - i +1;
if(qa[i].opt == 2) b[++bili] = (a[qa[i].a] += qa[i].b);
if(qa[i].opt == 3) bit[i] = 1;
}
s.Init(); solve(0 , q+1 , 1 , m);
sort(b+1,b+1+bili);
en = unique(b+1,b+1+bili);
int End = en - b - 1;
for(int i=1;i<=m;i++) c[i] = (node){1 , e[i].u , e[i].v , e[i].tim};
int Ti = m;
for(int i=1;i<=q;i++)
{
if(qa[i].opt!= 1) c[++Ti] = (node){qa[i].opt , qa[i].a , qa[i].b , q - i + 1};
}
sort(c+1,c+1+Ti , [&](node A,node B){return A.tim < B.tim;});
s1.Init();
for(int i=1;i<=n;i++){
segT.update(segT.rot[i]=segT.New(1,End), True(a[i]) , 1);
}
for(int i=1;i<=Ti;i++)
{
if(c[i].opt == 1) s1.Merge(s1.f(c[i].a) ,s1.f( c[i].b));
if(c[i].opt == 2)
{
int ftop = s1.f(c[i].a);
segT.update(segT.rot[ftop],True(a[c[i].a]) , -1);
a[c[i].a] -= c[i].b;
segT.update(segT.rot[ftop] , True(a[c[i].a]) , 1);
}
if(c[i].opt == 3)
{
int ftop = s1.f(c[i].a);
ans[q - c[i].tim + 1] = segT.rot[ftop]->query(c[i].b);
}
}
for(int i=1;i<= q;i++) if(bit[i]) printf("%lld
",ans[i]);
return 0;
}
在役一年我就没写过封装的这么严重的题 , 代码难度真是能和维护数列平分秋风
这个可以说是整体二分里面的天花板之一了