[Luogu P4230]连环病原体

博客围绕边编号区间环问题展开,题目是对n个点、m条边编号,若编号区间[l,r]内有环,边的val加1并输出。解题思路是利用区间包含关系,通过二维差分处理val值,用双指针找有环区间,最后给出代码转载链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

\(n\)个点,\(m\)条边,对每条边进行编号,若编号为\([l,r]\)的区间内出现了环,则给每一条边的\(val\)\(1\),最后输出每条边的\(val\)

大体思路

注意到:若\([l,r]\)之间存在环,则区间\([l,r]\)到区间\([l,m]\)都包含有环。

再整理一下得到:

  • 给区间\([l,r]\)中的边,每条边的\(val\)都加上"\(m-r+1\)"

  • 给区间\([r+1,m]\)中的边,加上一个首相为"\(m-r\)",公差为"\(-1\)"的等差数列。

这个显然可以用二维差分得到。

那么现在的问题就是:如何找到又环的区间?

很简单,我们可以用双指针,因为随着 \(l\) 的增大,\(r\) 必然不会减小。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=400005;
struct edge{
    int u,v;
} e[N];
ll delta1[N],delta2[N];
int val[N],fa[N],rev[N],ch[N][2];
int m,q,opt;
int wh(int x){return ch[fa[x]][1]==x;}
bool isrt(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
void rever(int x){rev[x]^=1,swap(ch[x][0],ch[x][1]);}
void pushdown(int x){
    if (rev[x]){
        if (ch[x][0]) rever(ch[x][0]);
        if (ch[x][1]) rever(ch[x][1]);
        rev[x]=0;
    }
}
void Allpushdown(int x){
    if (!isrt(x)) Allpushdown(fa[x]);
    pushdown(x);
}
void rotate(int x){
    int y=fa[x],z=fa[y],c=wh(x);
    if (!isrt(y)) ch[z][wh(y)]=x;
    fa[x]=z;
    ch[y][c]=ch[x][c^1];
    fa[ch[y][c]]=y;
    ch[x][c^1]=y;
    fa[y]=x;
}
void splay(int x){
    Allpushdown(x);
    for (;!isrt(x);rotate(x))
        if (!isrt(fa[x])) rotate(wh(fa[x])==wh(x)?fa[x]:x);
}
void access(int x){
    for (int y=0;x;y=x,x=fa[x]) splay(x),ch[x][1]=y;
}
void makert(int x){
    access(x),splay(x),rever(x);
}
int findrt(int x){
    access(x),splay(x);
    while (ch[x][0]) pushdown(x),x=ch[x][0]; 
    splay(x);
    return x;
}
void link(int x,int y){
    makert(x),fa[x]=y;
}
void cut(int x,int y){
    makert(x),access(y),splay(y);
    if (ch[y][0]!=x&&!ch[ch[y][0]][1]) return;
    fa[x]=ch[y][0]=0;
}
void query(int x,int y){
    makert(x),access(y),splay(y);
}
void solve(int l,int r,ll head,ll delta){
    if (l>r) return;
    delta1[l]+=head;
    delta1[r+1]+=-head;
    delta2[l+1]+=delta;
    delta2[r+1]+=-delta;
}
int main(){
    scanf("%d",&m);
    for (int i=1;i<=m;i++) scanf("%d%d",&e[i].u,&e[i].v);
    int r=1;
    for (int l=1;l<=m;l++){
        while (r<=m){
            int u=e[r].u,v=e[r].v;
            if (findrt(u)==findrt(v)){
                solve(l,r,m-r+1,0);
                solve(r+1,m,m-r,-1);
                break;
            } else link(u,v);
            ++r;
        }
        cut(e[l].u,e[l].v);
    }
    ll x=0,y=0;
    for (int i=1;i<=m;i++){
        x+=delta1[i],y+=delta2[i],x+=y;
        printf("%lld ",x);
    }
    puts("");
    return 0;
}

转载于:https://www.cnblogs.com/WR-Eternity/p/10899341.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值