zoukankan      html  css  js  c++  java
  • [BZOJ2959] 长跑

    Description

      某校开展了同学们喜闻乐见的阳光长跑活动。为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动。一时间操场上熙熙攘攘,摩肩接踵,盛况空前。
      为了让同学们更好地监督自己,学校推行了刷卡机制。
      学校中有n个地点,用1到n的整数表示,每个地点设有若干个刷卡机。
      有以下三类事件:
      1、修建了一条连接A地点和B地点的跑道。
      2、A点的刷卡机台数变为了B。
      3、进行了一次长跑。问一个同学从A出发,最后到达B最多可以刷卡多少次。具体的要求如下:
      当同学到达一个地点时,他可以在这里的每一台刷卡机上都刷卡。但每台刷卡机只能刷卡一次,即使多次到达同一地点也不能多次刷卡。
      为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。

    Input

      输入的第一行包含两个正整数n,m,表示地点的个数和操作的个数。
      第二行包含n个非负整数,其中第i个数为第个地点最开始刷卡机的台数。
      接下来有m行,每行包含三个非负整数P,A,B,P为事件类型,A,B为事件的两个参数。
      最初所有地点之间都没有跑道。
      每行相邻的两个数之间均用一个空格隔开。表示地点编号的数均在1到n之间,每个地点的刷卡机台数始终不超过10000,P=1,2,3。

    Output

      输出的行数等于第3类事件的个数,每行表示一个第3类事件。如果该情况下存在一种设定跑道方向的方案和路径的方案,可以到达,则输出最多可以刷卡的次数。如果A不能到达B,则输出-1。

    Solution

    注意到在一个双联通分量中每个点都可以被走到,可以把原图缩点,然后求树上距离就好了。

    那么可以用(LCT+)并查集维护边双联通分量。

    可能是我写丑了...我(find\_root)函数跑的特别慢...优化的话比较简单,对(splay)上每个点记一个(mn[x])表示(splay)上的(x)子树最靠左边那个点是什么,我就因为改了这个快了将近(10)倍....

    #include<bits/stdc++.h>
    using namespace std;
    
    void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
     
    void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(!x) return ;print(x/10),putchar(x%10+48);
    }
    void write(int x) {if(!x) putchar('0');else print(x);putchar('
    ');}
    
    const int maxn = 4e5+10;
    
    int n,m,v[maxn],top;
    int fa[maxn],son[maxn][2],f[maxn],sum[maxn],val[maxn],rev[maxn],sta[maxn],mn[maxn];
    
    #define ls son[x][0]
    #define rs son[x][1]
    
    int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
    
    int which(int x) {return son[fa[x]][1]==x;}
    int nrt(int x) {return son[fa[x]][0]==x||son[fa[x]][1]==x;}
    
    void update(int x) {
    	sum[x]=sum[ls]+sum[rs]+val[x];
    	mn[x]=ls?mn[ls]:x;
    }
    
    void push_rev(int x) {swap(ls,rs),rev[x]^=1;}
    void pushdown(int x) {if(rev[x]) push_rev(ls),push_rev(rs),rev[x]=0;}
    
    void rotate(int x) {
    	int y=fa[x],z=fa[y],w=which(x);
    	if(nrt(y)) son[z][son[z][1]==y]=x;
    	fa[x]=z,fa[y]=x,fa[son[x][w^1]]=y,son[y][w]=son[x][w^1],son[x][w^1]=y;
    	update(y),update(x);
    }
    
    void splay(int x) {
    	int t=x;while(nrt(t)) sta[++top]=t,t=fa[t];sta[++top]=t;
    	while(top) pushdown(sta[top--]);
    	for(;nrt(x);rotate(x)) if(nrt(fa[x])) rotate(which(fa[x])==which(x)?fa[x]:x);
    	update(x);
    }
    
    void access(int x) {for(int t=0;x;splay(x),rs=t,update(t=x),x=fa[x]=find(fa[x]));}
    
    void make_root(int x) {access(x),splay(x),push_rev(x);}
    
    int find_root(int x) {access(x),splay(x);return mn[x];}
    
    void del(int x,int y) {
    	if(x) val[y]+=val[x],f[x]=y,del(ls,y),del(rs,y);
    }
    
    void link(int x,int y) {
    	x=find(x),y=find(y);
    	if(x==y) return ;
    	make_root(x);
    	if(find_root(y)!=x) fa[x]=y;
    	else splay(x),del(rs,x),rs=0,update(x);
    }
    
    void modify(int x,int r) {
    	int u=find(x);splay(u);
    	val[u]+=r-v[x];v[x]=r;update(u);
    }
    
    int query(int x,int y) {
    	x=find(x),y=find(y);
    	make_root(x);
    	if(x==y) return val[x];
    	if(find_root(y)!=x) return -1;
    	access(y),splay(y);return sum[y];
    }
    
    int main() {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) f[i]=i;
    	for(int i=1;i<=n;i++) read(v[i]),val[i]=v[i];
    	for(int i=1,op,x,y;i<=m;i++) {
    		read(op),read(x),read(y);
    		if(op==1) link(x,y);
    		else if(op==2) modify(x,y);
    		else write(query(x,y));
    	}
    	return 0;
    }
    
  • 相关阅读:
    linux内核同步机制相关收集
    【转载】关于终端和控制台的一些解释
    【转】linux内核调试方法总结
    misc汇总
    关于proc的介绍,比较详细
    linux内核启动流程分析
    linux通用中断子系统介绍
    ftrace在mips上的验证
    线程死锁demo
    BeanFactory与FactoryBean
  • 原文地址:https://www.cnblogs.com/hbyer/p/10483960.html
Copyright © 2011-2022 走看看