[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. 在使用Git提交代码的时候犯了个低级错误

    今天在使用git提交代码的时候,犯了个很低级的错误,按照一切流程当我add并commit提交代码,最后使用push到远程仓库, 接下来奇怪的事情发生了,push之后,查看远程仓库代码并没有发现提交记录 ...

  2. 并发编程之 ConcurrentLinkedQueue 源码剖析

    前言 今天我们继续分析 java 并发包的源码,今天的主角是谁呢?ConcurrentLinkedQueue,上次我们分析了并发下 ArrayList 的替代 CopyOnWriteArrayList ...

  3. 并发编程之 Java 内存模型 + volatile 关键字 + Happen-Before 规则

    前言 楼主这个标题其实有一种作死的味道,为什么呢,这三个东西其实可以分开为三篇文章来写,但是,楼主认为这三个东西又都是高度相关的,应当在一个知识点中.在一次学习中去理解这些东西.才能更好的理解 Jav ...

  4. Visual studio 2017 Installer 打包.netframework

    前几步和网上其他教程一样的.主要是把.net framework 打包进安装程序里,如果选的是“从与我的应用程序相同的位置下载系统必备组件”,会提示 ERROR: 要在“系统必备”对话框中启用“从与我 ...

  5. JS DOM操作(五) Window.docunment对象——操作元素

    定位: var a = document.getElementByIt( "id" ) 同辈元素 var b = a.nextSibling;            -- 找 a ...

  6. 检测SQLServer复制订阅进度

    --检测复制订阅进度 --SQLSERVER2012 --创建链接服务器ReadServer,WriteServer,它们分别链接发布服务器.订阅服务器 --修改数据库名称 --在分发服务器执行 US ...

  7. 简单的redis缓存操作(get、put)

    简单的redis缓存操作(get.put) 本文介绍简单的redis缓存操作,包括引入jedisjar包.配置redis.RedisDao需要的一些工具.向redis中放数据(put).从redis中 ...

  8. 教你用Cordova打包Vue项目

    现在国内越来越多的开发者使用Vue开发混合app,但是当大家开发完成过后才发现不知道该怎么将Vue项目打包成app. 据我现在的了解打包Vue项目目前流行的就是使用weex和cordova.weex是 ...

  9. Code Signal_练习题_Knapsack Light

    You found two items in a treasure chest! The first item weighs weight1 and is worth value1, and the ...

  10. mysql时间与字符串之间相互转换

    1.时间转字符串 DATE_FORMAT(日期,格式字符串) SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'); 2.字符串转时间 STR_TO_DATE( ...