• [Luogu P4230]连环病原体


    题目大意

    (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;
    }
    
  • 相关阅读:
    电话号码和手机号码正则
    IntelliJ Idea 常用快捷键
    springboot常用注解
    idea常用快捷键和插件
    百度地图API的使用方法
    js 经常用到的键盘码
    https://www.cnblogs.com/
    axios传参 后台接收为空
    面相对象之继承
    初始面向对象
  • 原文地址:https://www.cnblogs.com/WR-Eternity/p/10899341.html
Copyright © 2020-2023  润新知