[luogu P4230] 连环病原体

题意

给定一个长度为 \(n\) 的边序列, 当这个序列的一个子区间内的边都加入图中时产生了环则称其为"加强区间", 求序列中的每条边在多少个加强区间中.

\(n\le 4\times 10^5,|V|\le 2\times 10^5\).

题解

想打休闲板子于是想起了这道上古XCR题...XCR #12好像要咕?

首先有一个显然的沙雕性质: 如果一个子区间是加强区间, 那么所有包含这个子区间的区间也是加强区间.

接着我们按照套路, 求所有以 \(i\) 为右端点的加强区间对答案产生的贡献. 这部分需要快速求.

根据上面的沙雕性质, 我们只要求出以 \(i\) 为右端点的最短加强区间的左端点 \(l\), 那么所有小于 \(l\) 的左端点也是加强区间. 于是对于所有坐标为 \(k\) 且 \(k<l\) 的位置都会产生 \(k\) 的贡献, 而对 \(k\in [l,i]\) 则会产生 \(l\) 的贡献. 于是就变成了区间加等差数列单点求值, 随手拉个线段树就可以搞了.

问题变成怎么求最短加强区间. 不难发现因为有上面的沙雕单调性直接双指针就可以了. 但是加边/删边是随机的, 所以只能用LCT维护.

跑得巨快无比. 虽然数据范围有 \(2\times 10^5\) 个点但是我的常数大如狗的LCT板子极限数据才跑了 \(300\texttt{ms}\)...

记得当时毒瘤oscar造数据的时候专门卡了查完根不Splay的选手233333

参考代码

#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);
}
}

[luogu P4230]连环病原体的更多相关文章

  1. LCT总结

    LCT总结 类比树剖,树剖是通过静态地把一棵树剖成若干条链然后用一种支持区间操作的数据结构维护(比如线段树.树状数组),而LCT是动态地去处理这个问题. 大家都知道树剖用线段树维护,而LCT用\(sp ...

  2. LCT[Link-Cut-Tree学习笔记]

    部分摘抄于 FlashHu candy99 所以文章篇幅较长 请有足够的耐心(不是 其实不用学好splay再学LCT的-/kk (至少现在我平衡树靠fhq) 如果学splay的话- 也许我菜吧-LCT ...

  3. luogu P1526 [NOI2003]智破连环阵 搜索+最大匹配+剪枝

    LINK:智破连环阵 考试的时候 题意没理解清楚 题目是指一个炸弹爆炸时间结束后再放另一个炸弹 而放完一个炸弹紧接另一个炸弹.题目中存在然后二字. 这样我们可以发现某个炸弹只会炸连续的一段. 但是 由 ...

  4. Luogu 魔法学院杯-第二弹(萌新的第一法blog)

    虽然有点久远  还是放一下吧. 传送门:https://www.luogu.org/contest/show?tid=754 第一题  沉迷游戏,伤感情 #include <queue> ...

  5. luogu p1268 树的重量——构造,真正考验编程能力

    题目链接:http://www.luogu.org/problem/show?pid=1268#sub -------- 这道题费了我不少心思= =其实思路和标称毫无差别,但是由于不习惯ACM风格的题 ...

  6. [luogu P2170] 选学霸(并查集+dp)

    题目传送门:https://www.luogu.org/problem/show?pid=2170 题目描述 老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一 ...

  7. [luogu P2647] 最大收益(贪心+dp)

    题目传送门:https://www.luogu.org/problem/show?pid=2647 题目描述 现在你面前有n个物品,编号分别为1,2,3,--,n.你可以在这当中任意选择任意多个物品. ...

  8. Luogu 考前模拟Round. 1

    A.情书 题目:http://www.luogu.org/problem/show?pid=2264 赛中:sb题,直接暴力匹配就行了,注意一下读入和最后一句话的分句 赛后:卧槽 怎么只有40 B.小 ...

  9. luogu P2580 于是他错误的点名开始了

    luogu  P2580 于是他错误的点名开始了 https://www.luogu.org/problem/show?pid=2580 题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 他会一边 ...

随机推荐

  1. 使用clamav模块对数据流进行病毒检测

    准备工作:linux下安装clamav成功,启动clamav并打开本地socket监听"/tmp/clamav.socket" clamav开源工程目录:/usr/local/ 修 ...

  2. PHP7最高性能优化建议

    PHP7已经发布了, 作为PHP10年来最大的版本升级, 最大的性能升级, PHP7在多放的测试中都表现出很明显的性能提升, 然而, 为了让它能发挥出最大的性能, 我还是有几件事想提醒下. PHP7 ...

  3. GCD之定时器dispatch_source_t(转载暂时未完全理解)

    #import "ViewController.h" @interface ViewController (){ IBOutlet UIButton *l_timeButton; ...

  4. CentOS 7.2安装Docker-ce

    1.Docker分类 Docker Engine改为Docker CE(社区版) 它包含了CLI客户端.后台进程/服务以及API.用户像以前以同样的方式获取.Docker Data Center改为D ...

  5. RowFilter遇上特殊字符*%'[]\

    开发时,时不时需要分析DataView的数据.计算,过滤等 .但是处理的值有可能会遇上是带有特殊字符*.%.'.[.]和\ .需要手动在表达式中,处理这些特殊字符,需要再使用"[" ...

  6. textarea赋值时换行符无效的解决方法

    //替换字符串中的换行符为br var Content = JSON.stringify(txt); Content = Content.replace(/\\n/g, '<br />') ...

  7. JS生成指定范围内的随机数(支持随机小数)

    直接需要函数的话,直接到文章的最后面找. ============================================================= 转载:https://www.cn ...

  8. 【4】 .net MVC使用Session验证用户登录

    用最简单的Session方式记录用户登录状态 1.添加DefaultController控制器,重写OnActionExecuting方法,每次访问控制器前触发 public class Defaul ...

  9. Calendar类的一些不易区分的属性

    1.Calendar.MONTH 月份从0-11,获取之后需要加1才能得到真正的月份 2.Calendar.DAY_OF_WEEK 本周的第几天,从星期天开始算 3.Calendar.WEEK_OF_ ...

  10. python中强大优雅的列表推导表达式

    推导表达式其实就是简化一些循环判断操作等 生成一个数字1-10的列表,可以有多少种方法? >>> l = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] > ...