给定一个长度为 \(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); } }