給定一個長度爲 \(n\) 的邊序列, 當這個序列的一個子區間內的邊都加入圖中時產生了環則稱其爲"增強區間", 求序列中的每條邊在多少個增強區間中.c++
\(n\le 4\times 10^5,|V|\le 2\times 10^5\).this
想打休閒板子因而想起了這道上古XCR題...XCR #12好像要咕?spa
首先有一個顯然的沙雕性質: 若是一個子區間是增強區間, 那麼全部包含這個子區間的區間也是增強區間.指針
接着咱們按照套路, 求全部以 \(i\) 爲右端點的增強區間對答案產生的貢獻. 這部分須要快速求.code
根據上面的沙雕性質, 咱們只要求出以 \(i\) 爲右端點的最短增強區間的左端點 \(l\), 那麼全部小於 \(l\) 的左端點也是增強區間. 因而對於全部座標爲 \(k\) 且 \(k<l\) 的位置都會產生 \(k\) 的貢獻, 而對 \(k\in [l,i]\) 則會產生 \(l\) 的貢獻. 因而就變成了區間加等差數列單點求值, 隨手拉個線段樹就能夠搞了.blog
問題變成怎麼求最短增強區間. 不難發現由於有上面的沙雕單調性直接雙指針就能夠了. 可是加邊/刪邊是隨機的, 因此只能用LCT維護.ip
跑得巨快無比. 雖然數據範圍有 \(2\times 10^5\) 個點可是個人常數大如狗的LCT板子極限數據才跑了 \(300\texttt{ms}\)...get
記得當時毒瘤oscar造數據的時候專門卡了查完根不Splay的選手233333it
#include <bits/stdc++.h> const int MAXN=4e5+10; typedef long long intEx; struct LCT{ #define lch chd[0] #define rch chd[1] #define kch chd[k] #define xch chd[k^1] struct Node{ bool rev; Node* prt; Node* pprt; Node* chd[2]; Node():rev(false),prt(NULL),pprt(NULL),chd{NULL,NULL}{} void Flip(){ if(this!=NULL){ this->rev=!this->rev; std::swap(this->lch,this->rch); } } void PushDown(){ if(this!=NULL&&this->rev){ this->lch->Flip(); this->rch->Flip(); this->rev=false; } } }; std::vector<Node*> N; LCT(int n):N(n+1){ for(int i=1;i<=n;i++) N[i]=new Node(); } void Rotate(Node* root,int k){ Node* tmp=root->xch; root->PushDown(); tmp->PushDown(); tmp->prt=root->prt; if(root->prt==NULL){ tmp->pprt=root->pprt; root->pprt=NULL; } else if(root->prt->lch==root) root->prt->lch=tmp; else root->prt->rch=tmp; root->xch=tmp->kch; if(root->xch!=NULL) root->xch->prt=root; tmp->kch=root; root->prt=tmp; } void Splay(Node* root){ while(root->prt!=NULL){ int k=root->prt->lch==root; if(root->prt->prt==NULL) Rotate(root->prt,k); else{ int d=root->prt->prt->lch==root->prt; Rotate(k==d?root->prt->prt:root->prt,k); Rotate(root->prt,d); } } } void Expose(Node* root){ Splay(root); root->PushDown(); if(root->rch){ root->rch->pprt=root; root->rch->prt=NULL; root->rch=NULL; } } bool Splice(Node* root){ Splay(root); if(root->pprt==NULL) return false; Expose(root->pprt); root->pprt->rch=root; root->prt=root->pprt; root->pprt=NULL; return true; } void Access(Node* root){ Expose(root); while(Splice(root)); } void Evert(Node* root){ Access(root); root->Flip(); } void Link(int a,int b){ Node* x=N[a]; Node* y=N[b]; Evert(y); y->pprt=x; } void Cut(int a,int b){ Node* x=N[a]; Node* y=N[b]; Evert(x); Access(y); y->PushDown(); y->lch->prt=NULL; y->lch=NULL; } Node* FindRoot(int x){ Node* cur=N[x]; Access(cur); while(cur->lch) cur=cur->lch; Splay(cur); return cur; } #undef lch #undef rch #undef xch #undef kch }; struct Node{ int l; int r; intEx add; intEx delta; Node* lch; Node* rch; Node(int,int); void PushDown(); intEx Query(int); void Add(intEx,intEx); void Add(int,int,intEx,intEx); }; int n; int v; std::pair<int,int> E[MAXN]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&E[i].first,&E[i].second); v=std::max(E[i].first,v); v=std::max(E[i].second,v); } Node* N=new Node(1,n); LCT* T=new LCT(v); int l=0; for(int i=1;i<=n;i++){ while(T->FindRoot(E[i].first)==T->FindRoot(E[i].second)){ ++l; T->Cut(E[l].first,E[l].second); } T->Link(E[i].first,E[i].second); if(l!=0){ N->Add(l+1,i,l,0); N->Add(1,l,1,1); } } for(int i=1;i<=n;i++) printf("%lld%c",N->Query(i)," \n"[i==n]); return 0; } inline void Node::PushDown(){ if(this->add!=0||this->delta!=0){ this->lch->Add(this->add,this->delta); this->rch->Add(this->add+this->delta*(this->rch->l-this->l),this->delta); this->add=this->delta=0; } } inline void Node::Add(intEx a,intEx d){ if(this!=NULL){ this->add+=a; this->delta+=d; } } intEx Node::Query(int x){ if(this->l==this->r) return this->add; else{ this->PushDown(); if(x<=this->lch->r) return this->lch->Query(x); else return this->rch->Query(x); } } void Node::Add(int l,int r,intEx a,intEx d){ if(l<=this->l&&this->r<=r) this->Add(a+(this->l-l)*d,d); else{ this->PushDown(); if(l<=this->lch->r) this->lch->Add(l,r,a,d); if(this->rch->l<=r) this->rch->Add(l,r,a,d); } } Node::Node(int l,int r):l(l),r(r),add(0),delta(0),lch(NULL),rch(NULL){ if(l!=r){ int mid=(l+r)>>1; this->lch=new Node(l,mid); this->rch=new Node(mid+1,r); } }