zoukankan      html  css  js  c++  java
  • 连环病原体

    题目链接
    题意:一张 $ n $ 点 $ m $ 边无向图,每条边有编号。若一个区间内的边能连成一个环,则称这个区间为好区间。求每条边分别在多少个好区间内。

    算法一:
    首先想到一个暴力。枚举区间的左端点,右端点,用并查集判断是否有环,得分 $ 60 $ 分。

    算法二:
    显然的优化1:设 $ [l, r] $ 表示从 $ l $ 到 $ r $ 的区间满足条件且从 $ l $ 到 $ r - 1 $ 的区间不满足条件。 $ l $ 递增,显然 $ r $ 不降。
    显然的优化2:对于统计答案,记录首项和公差,最后跑一边前缀和。
    对于优化1,我们需要一个可以支持加边和删边的并查集, $ LCT $ 即可。
    得分 $ 90 $ ~ $ 100 $ 分( $ findroot $ 时将结果 $ splay $ 试试?)。

    #include<bits/stdc++.h>
    #define lc ch[u][0]
    #define rc ch[u][1]
    using namespace std;
    const int N=400010;
    typedef long long ll;
    int m,a[N],b[N];
    int fa[N],ch[N][2],sta[N];
    bool lz[N],flag;
    ll ans[N],dl[N];
    
    inline bool nroot(int u) { return ch[fa[u]][0]==u||ch[fa[u]][1]==u; }
    void pushdown(int u) { if(lz[u]) swap(lc,rc),lz[lc]^=1,lz[rc]^=1,lz[u]=0; }
    inline void pushup(int u) {  }
    void rotate(int u) {
    	int y=fa[u],z=fa[y],k=ch[y][1]==u,w=ch[u][k^1];
    	if(nroot(y)) ch[z][ch[z][1]==y]=u; ch[y][k]=w,ch[u][k^1]=y;
    	if(w) fa[w]=y; fa[y]=u,fa[u]=z;
    	pushup(y),pushup(u);
    }
    void splay(int u) {
    	int y=u,z,top=1;sta[top]=y;while(nroot(y)) sta[++top]=y=fa[y];
    	while(top) pushdown(sta[top--]);
    	for(;nroot(u);rotate(u)) {
    		y=fa[u],z=fa[y];
    		if(nroot(y)) rotate((ch[y][0]==u)^(ch[z][0]==y)? u:y);
    	}
    }
    void access(int u) { for(int y=0;u;u=fa[y=u]) splay(u),rc=y,pushup(u); }
    void makeroot(int u) { access(u),splay(u),lz[u]^=1; }
    int findroot(int u) {
    	access(u),splay(u),pushdown(u);
    	while(lc) u=lc,pushdown(u); splay(u);return u;
    }
    void split(int x,int y) { makeroot(x),access(y),splay(y); }
    void lnk(int x,int y) { makeroot(x),fa[x]=y; }
    void cut(int x,int y) { split(x,y),ch[y][0]=fa[x]=0,pushup(y); }
    int main() {
    	scanf("%d",&m);
    	for(int i=1;i<=m;i++) scanf("%d%d",&a[i],&b[i]);
    	for(int l=1,r=0;l<=m;++l) {
    		flag=0;
    		while(r<m) {
    			++r;
    			if(findroot(a[r])==findroot(b[r])) { flag=1;break; }
    			lnk(a[r],b[r]);
    		}
    		if(!flag) break;
    		ans[l]=m-r+1,--dl[r+1],--r,cut(a[l],b[l]);
    	}
    	ll sum=0;
    	for(int i=1;i<=m;i++) sum+=dl[i],ans[i]=ans[i-1]+ans[i]+sum;
    	for(int i=1;i<=m;i++) printf("%lld ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    android 显示自定义视图对话框
    android为按钮事件进行监听过程
    实验三
    实验二
    实验一
    第五次作业
    第四次作业
    第三次作业
    第二次作业
    第一次作业
  • 原文地址:https://www.cnblogs.com/daniel14311531/p/10263720.html
Copyright © 2011-2022 走看看