[BZOJ]2959: 长跑

题解:   我们考虑一条链时 答案为链上和  路径上有环时  这个环上的点都会产生贡献  然后我们可以 用并查集来维护LCT  有环时直接缩成一个点即可

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
const int MAXN=3e5+10;
const double eps=1e-8;
#define ll long long
using namespace std;
struct edge{int t,v;edge*next;}e[MAXN<<1],*h[MAXN],*o=e;
void add(int x,int y,int vul){o->t=y;o->v=vul;o->next=h[x];h[x]=o++;}
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int res[MAXN],pre[MAXN],ch[MAXN][2],key[MAXN],sum[MAXN];
int f[MAXN];
bool rt[MAXN];

int find1(int x){
    if(x==f[x])return x;
    return f[x]=find1(f[x]);
}

int a[MAXN];
void newnode(int t){
    res[t]=pre[t]=ch[t][0]=ch[t][1]=0;key[t]=sum[t]=a[t];rt[t]=1;
}

void reverse(int x){
    if(!x)return ;
    swap(ch[x][0],ch[x][1]);
    res[x]^=1;
}

void push(int x){
    if(res[x]){
    reverse(ch[x][0]);
    reverse(ch[x][1]);
    res[x]^=1;
    }
}

void up(int x){sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+key[x];}

void P(int x){
    if(!rt[x])P(find1(pre[x]));
    push(x);
}


void rotate(int x,int kind){
    int y=find1(pre[x]);
    ch[y][!kind]=ch[x][kind];pre[ch[x][kind]]=y;
    if(rt[y])rt[y]=0,rt[x]=1;
    else ch[find1(pre[y])][ch[find1(pre[y])][1]==y]=x;
    pre[x]=find1(pre[y]);ch[x][kind]=y;pre[y]=x;
    up(y);
}

void splay(int x){
    P(x);
    while(!rt[x]){
    if(rt[find1(pre[x])])rotate(x,ch[find1(pre[x])][0]==x);
    else{
        int y=find1(pre[x]);int kind=ch[find1(pre[y])][0]==y;
        if(ch[y][kind]==x)rotate(x,!kind),rotate(x,kind);
        else rotate(y,kind),rotate(x,kind);
    }
    }
    up(x);
}

void access(int x){
    int y=0;
    while(x){
    splay(x);
    if(ch[x][1])rt[ch[x][1]]=1,pre[ch[x][1]]=x,ch[x][1]=0;
    if(rt[y])rt[y]=0;
    ch[x][1]=y;up(x);
    y=x;x=find1(pre[x]);
    }
}

bool pd(int u,int v){
    u=find1(u);v=find1(v);
    while(pre[u])u=find1(pre[u]);
    while(pre[v])v=find1(pre[v]);
    return u==v;
}

void mroot(int u){access(u);splay(u);reverse(u);}

int st[MAXN],tot;

void dfs(int x){
    if(!x)return ;
    st[++tot]=x;
    push(x);
    dfs(ch[x][0]);
    dfs(ch[x][1]);
}

void Tarjan(int u,int v){
    u=find1(u);v=find1(v);
    mroot(u);access(v);splay(v);
    tot=0;dfs(v);
    int ans=key[st[1]];
    //if(tot==1)cout<<"sb"<<endl;
    inc(i,2,tot){
    ans+=key[st[i]];
    int t1=find1(st[i-1]);
    int t2=find1(st[i]);
    if(t1==t2)continue;
    if(t1>t2)swap(t1,t2);
    f[t2]=t1;
    }
    inc(i,1,tot)rt[st[i]]=1,ch[st[i]][0]=ch[st[i]][1]=0,pre[st[i]]=0;
    key[find1(st[1])]=sum[find1(st[1])]=ans;
    splay(find1(st[1]));
    up(find1(st[1]));
}

void update(int x,int k){
    int t=find1(x);
    access(t);splay(t);key[t]+=(k-a[x]);
    a[x]=k;up(t);
}

void Link(int u,int v){
    u=find1(u);v=find1(v);
    mroot(u);mroot(v);
    pre[u]=v;
}

int query(int u,int v){
    if(!pd(u,v))return -1;
    int t1=find1(u);int t2=find1(v);
    if(t1==t2)return key[t1];
    mroot(t1);access(t2);splay(t2);
    return sum[t2];
}

int main(){
    int n=read();int m=read();
    inc(i,1,n)a[i]=read(),newnode(i),f[i]=i;
    int op,x,y;
    while(m--){
    op=read();x=read();y=read();
    if(op==1){
        if(find1(x)==find1(y))continue;
        if(pd(x,y))Tarjan(x,y);
        else Link(x,y);
    }
    else if(op==2)update(x,y);
    else printf("%d
",query(x,y));
    }
    return 0;
}

  

2959: 长跑

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 1244  Solved: 614
[Submit][Status][Discuss]

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。

Sample Input

9 31
10 20 30 40 50 60 70 80 90
3 1 2
1 1 3
1 1 2
1 8 9
1 2 4
1 2 5
1 4 6
1 4 7
3 1 8
3 8 8
1 8 9
3 8 8
3 7 5
3 7 3
1 4 1
3 7 5
3 7 3
1 5 7
3 6 5
3 3 6
1 2 4
1 5 5
3 3 6
2 8 180
3 8 8
2 9 190
3 9 9
2 5 150
3 3 6
2 1 210
3 3 6

Sample Output


-1
-1
80
170
180
170
190
170
250
280
280
270
370
380
580

HINT

数据规模及约定

  对于100%的数据,m<=5n,任意时刻,每个地点的刷卡机台数不超过10000。N<=1.5×105

原文地址:https://www.cnblogs.com/wang9897/p/10361996.html