2959: 长跑
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 315 Solved: 178
[ 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
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
Source
题解:LCT+并查集
LCT维护双联通性。
这道题如果没有加边操作,那么可以用tarjan缩点然后建树链剖。但是加边的话会使原本构不成环的部分构成新的环所以考虑在树上用LCT进行动态的缩点和维护操作。
如果加入某条边之前两个端点就联通,那么我们就将两个点之间的路径提出来,加入改边会使这条路径上的点可以互相到达,所以我们把这些点缩成一个代表节点,然后将权值都加给这个节点。并用另外一个并查集维护每个点所属的代表元素信息。
对于询问2,其实就变成了从LCT中提取缩点后两点所属的点之间的路径上的权值总和。
写LCT是需要注意,以前找的是fa[x],现在应该是找findset(fa[x]),就是fa[x]所属的缩点的代表元素。
因为每个点最多只会被合并一次,所以我们的时间复杂度仍然是O(MlogN),但是常数巨大,所以能不findset就不要findset!!!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 150003
using namespace std;
int n,m,top,st[N],father[N],rev[N],a[N];
int belong[N],fa[N],ch[N][2],val[N],mx[N];
int read()
{
char c=getchar();
for (;c>'9'||c<'0';c=getchar());
int num=0;
for (;c>='0'&&c<='9';c=getchar())
num=num*10+c-'0';
return num;
}
int findset(int x)
{
if (belong[x]==x) return x;
belong[x]=findset(belong[x]);
return belong[x];
}
int find(int x)
{
if (father[x]==x) return x;
father[x]=find(father[x]);
return father[x];
}
int isroot(int x)
{
int f=findset(fa[x]);
return ch[f][1]!=x&&ch[f][0]!=x;
}
int get(int x)
{
return ch[findset(fa[x])][1]==x;
}
void update(int x)
{
mx[x]=val[x];
if (ch[x][0]) mx[x]+=mx[ch[x][0]];
if (ch[x][1]) mx[x]+=mx[ch[x][1]];
}
void pushdown(int x)
{
if (rev[x]){
swap(ch[x][0],ch[x][1]);
rev[ch[x][1]]^=1; rev[ch[x][0]]^=1;
rev[x]=0;
}
}
void rotate(int x)
{
int y=fa[x]; int z=fa[y]; int which=get(x);
if (!isroot(y)) ch[z][ch[z][1]==y]=x;
ch[y][which]=ch[x][which^1]; fa[ch[x][which^1]]=y;
ch[x][which^1]=y; fa[y]=x; fa[x]=z;
update(y); update(x);
}
void splay(int x)
{
top=0; st[++top]=x;
for (int i=x;!isroot(i);i=findset(fa[i]))
st[++top]=findset(fa[i]);
for (int i=top;i>=1;i--) pushdown(st[i]),fa[st[i]]=findset(fa[st[i]]);
int y;
while (!isroot(x)){
y=fa[x];
if (!isroot(y)) rotate(get(x)==get(y)?y:x);
rotate(x);
}
}
void access(int x)
{
int t=0;
while (x){
splay(x);
ch[x][1]=t;
update(x);
t=x; x=findset(fa[x]);
}
}
void rever(int x)
{
access(x); splay(x); rev[x]^=1;
}
void link(int x,int y)
{
rever(x); fa[x]=y;
}
void merge(int x,int y)
{
belong[findset(x)]=findset(y); pushdown(x);
if (x!=y) val[y]+=val[x];
//fa[x]=0;
if (ch[x][0]) merge(ch[x][0],y);
if (ch[x][1]) merge(ch[x][1],y);
ch[x][1]=ch[x][0]=0;
}
int main()
{
freopen("run.in","r",stdin);
freopen("run.out","w",stdout);
n=read();
m=read();
for (int i=1;i<=n;i++){
val[i]=read(); a[i]=val[i];
belong[i]=i; father[i]=i;
}
for (int i=1;i<=m;i++) {
int opt,x,y; opt=read(); x=read(); y=read();
if (opt==1) {
x=findset(x); y=findset(y);
if (x==y) continue;
int r1=find(x); int r2=find(y);
if (r1!=r2) {
father[r2]=r1;
link(x,y);
}
else {
rever(x); access(y); splay(y);
merge(y,y); update(y);
}
}
if (opt==2) {
int xx=x;
x=findset(x);
access(x); splay(x); val[x]+=y-a[xx]; a[xx]=y;
update(x);
}
if (opt==3) {
x=findset(x); y=findset(y);
if (find(x)!=find(y)) {
printf("-1\n");
continue;
}
rever(x); access(y); splay(y); printf("%d\n",mx[y]);
}
}
}