2594: [Wc2006]水管局长数据加强版
Time Limit: 25 Sec Memory Limit: 128 MBSubmit: 1278 Solved: 404
[ Submit][ Status][ Discuss]
Description
SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。
在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于MY市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。
不妨将MY市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。
Input
输入文件第一行为3个整数:N, M, Q分别表示管道连接处(结点)的数目、目前水管(无向边)的数目,以及你的程序需要处理的任务数目(包括寻找一条满足要求的路径和接受某条水管坏掉的事实)。
以下M行,每行3个整数x, y和t,描述一条对应的水管。x和y表示水管两端结点的编号,t表示准备送水所需要的时间。我们不妨为结点从1至N编号,这样所有的x和y都在范围[1, N]内。
以下Q行,每行描述一项任务。其中第一个整数为k:若k=1则后跟两个整数A和B,表示你需要为供水公司寻找一条满足要求的从A到B的水管路径;若k=2,则后跟两个整数x和y,表示直接连接x和y的水管宣布报废(保证合法,即在此之前直接连接x和y尚未报废的水管一定存在)。
Output
按顺序对应输入文件中每一项k=1的任务,你需要输出一个数字和一个回车/换行符。该数字表示:你寻找到的水管路径中所有管道全都完成准备工作所需要的时间(当然要求最短)。
Sample Input
4 4 3
1 2 2
2 3 3
3 4 2
1 4 2
1 1 4
2 1 4
1 1 4
1 2 2
2 3 3
3 4 2
1 4 2
1 1 4
2 1 4
1 1 4
Sample Output
2
3
【原题数据范围】
N ≤ 1000
M ≤ 100000
Q ≤ 100000
测试数据中宣布报废的水管不超过5000条;且任何时候我们考虑的水管网络都是连通的,即从任一结点A必有至少一条水管路径通往任一结点B。
【加强版数据范围】
N ≤ 100000
M ≤ 1000000
Q ≤ 100000
任何时候我们考虑的水管网络都是连通的,即从任一结点A必有至少一条水管路径通往任一结点B。
【C/C++选手注意事项】
由于此题输入规模较大(最大的测试点约20MB),因此即使使用scanf读入数据也会花费较多的时间。为了节省读入耗时,建议使用以下函数读入正整数(返回值为输入文件中下一个正整数):
int getint()
{
char ch = getchar();
for ( ; ch > '9' || ch < '0'; ch = getchar());
int tmp = 0;
for ( ; '0' <= ch && ch <= '9'; ch = getchar())
tmp = tmp * 10 + int(ch) - 48;
return tmp;
}
3
【原题数据范围】
N ≤ 1000
M ≤ 100000
Q ≤ 100000
测试数据中宣布报废的水管不超过5000条;且任何时候我们考虑的水管网络都是连通的,即从任一结点A必有至少一条水管路径通往任一结点B。
【加强版数据范围】
N ≤ 100000
M ≤ 1000000
Q ≤ 100000
任何时候我们考虑的水管网络都是连通的,即从任一结点A必有至少一条水管路径通往任一结点B。
【C/C++选手注意事项】
由于此题输入规模较大(最大的测试点约20MB),因此即使使用scanf读入数据也会花费较多的时间。为了节省读入耗时,建议使用以下函数读入正整数(返回值为输入文件中下一个正整数):
int getint()
{
char ch = getchar();
for ( ; ch > '9' || ch < '0'; ch = getchar());
int tmp = 0;
for ( ; '0' <= ch && ch <= '9'; ch = getchar())
tmp = tmp * 10 + int(ch) - 48;
return tmp;
}
思路:个人感觉这个题很好。
我们可以把所有的操作存下来,倒序进行,这样就把删边变成加边了,然后把边权变成点权,这样如果u跟v之间的边(假设编号为x)就可以变成(u,x+N)和(v,x+N)两条,便与处理。
首先把没有删除的边加上,加边的时候按照最小生成树kruskal加边,这样保证加进去的边最小,以满足题意,并且保证没有环。然后进行操作2,如果加进去的边的权值小于当前路径上最大值的最小值,则把原来最大值删掉(也就是切除原来的(u,x+N)和(v,x+N)),然后加上新加入的边,查询的时候直接进行就可以了
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int INF=1000000000;
const int maxn=1500100;
int N,M,Q;
template <class T>
inline bool scan_d(T &ret)
{
char c;
int sgn;
if(c=getchar(),c==EOF) return 0; //EOF
while(c!='-'&&(c<'0'||c>'9')) c=getchar();
sgn=(c=='-')?-1:1;
ret=(c=='-')?0:(c-'0');
while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
ret*=sgn;
return 1;
}
int f[maxn];
struct Edge
{
int u,v,w,id;
bool de;
}edge[maxn];
struct Qu
{
int id;
int ans;
int type,x,y;
}q[maxn];
//并查集操作,最小生成树
bool cmp1(Edge A,Edge B)
{
return A.w<B.w;
}
bool cmp2(Edge A,Edge B)
{
if(A.u==B.u)return A.v<B.v;
return A.u<B.u;
}
bool cmp3(Edge A,Edge B)
{
return A.id<B.id;
}
struct LCT
{
int ch[maxn][2],pre[maxn],key[maxn];
bool rev[maxn];
int maxv[maxn];
bool rt[maxn];
int top,s[maxn];
void update_rev(int r)
{
if(!r)return;
swap(ch[r][0],ch[r][1]);
rev[r]^=1;
}
void pushdown(int r)
{
if(rev[r])
{
update_rev(ch[r][1]);
update_rev(ch[r][0]);
rev[r]=0;
}
}
void pushup(int r)
{
maxv[r]=r;
if(key[maxv[ch[r][0]]]>key[maxv[r]])maxv[r]=maxv[ch[r][0]];
if(key[maxv[ch[r][1]]]>key[maxv[r]])maxv[r]=maxv[ch[r][1]];
}
void rotate(int x)
{
int y=pre[x],kind=ch[y][1]==x;
ch[y][kind]=ch[x][!kind];
pre[ch[y][kind]]=y;
pre[x]=pre[y];
pre[y]=x;
ch[x][!kind]=y;
if(rt[y])rt[y]=false,rt[x]=true;
else ch[pre[x]][ch[pre[x]][1]==y]=x;
pushup(y);
}
//将根节点到r的路径上的所有及诶单的标记下方
void P(int r)
{
if(!rt[r])P(pre[r]);
pushdown(r);
}
void Splay(int r)
{
top=0;s[++top]=r;
for(int i=r;!rt[i];i=pre[i])
s[++top]=pre[i];
for(int i=top;i;i--)
pushdown(s[i]);
while(!rt[r])
{
int f=pre[r],ff=pre[f];
if(rt[f])rotate(r);
else if((ch[ff][1]==f)==(ch[f][1]==r))
rotate(f),rotate(r);
else rotate(r),rotate(r);
}
pushup(r);
}
int Access(int x)
{
int y=0;
for(;x;x=pre[y=x])
{
Splay(x);
rt[ch[x][1]]=true,rt[ch[x][1]=y]=false;
pushup(x);
}
return y;
}
int getroot(int x)
{
Access(x);
Splay(x);
while (ch[x][0])
x = ch[x][0];
return x;
}
//判断是否同根
bool judge(int u,int v)
{
while(pre[u])u=pre[u];
while(pre[v])v=pre[v];
return u==v;
}
//将r变成他所在根
void mroot(int r)
{
Access(r);
Splay(r);
update_rev(r);
}
//调用后u是原来u和v的lca,v和ch[u][1]分别存折lca的两个儿子
void lca(int &u,int &v)
{
Access(v),v=0;
while(u)
{
Splay(u);
if(!pre[u])return;
rt[ch[u][1]]=true;
rt[ch[u][1]=v]=false;
pushup(u);
u=pre[v=u];
}
}
//将u合并到v上
void link(int u,int v)
{
mroot(u);
pre[u]=v;
}
//将v和他的父节点分离
void cut(int u,int v)
{
mroot(u);
Access(v);
Splay(v);
pre[ch[v][0]]=0;
pre[v]=0;
rt[ch[v][0]]=true;
ch[v][0]=0;
pushup(v);
}
void init()
{
for(int i=0;i<=M+N;i++)rt[i]=true;
}
int find(int x,int y)
{
int l=1,r=M;
while(l<=r)
{
int mid=(l+r)>>1;
if(edge[mid].u==x&&edge[mid].v==y)return mid;
if(edge[mid].u>x||(edge[mid].u==x&&edge[mid].v>y))r=mid-1;
else l=mid+1;
}
}
int query(int x,int y)
{
mroot(x);
Access(y);
Splay(y);
return maxv[y];
}
}tree;
int getfather(int x)
{
if(x==f[x])return x;
return f[x]=getfather(f[x]);
}
int main()
{
scan_d(N);scan_d(M);scan_d(Q);
tree.init();
for(int i=0;i<=N;i++)f[i]=i;
for(int i=1;i<=M;i++)
{
scan_d(edge[i].u);scan_d(edge[i].v);scan_d(edge[i].w);
if(edge[i].u>edge[i].v)swap(edge[i].u,edge[i].v);
}
//按照权值排序,进行编号,以备使用
sort(edge+1,edge+1+M,cmp1);
for(int i=1;i<=M;i++)
{
edge[i].id=i;
tree.key[N+i]=edge[i].w;
tree.maxv[N+i]=N+i;//最长边的编号
}
//排序,二分查找删除的边的编号
sort(edge+1,edge+1+M,cmp2);
for(int i=1;i<=Q;i++)
{
scan_d(q[i].type);scan_d(q[i].x);scan_d(q[i].y);
if(q[i].type==2)
{
if(q[i].x>q[i].y)swap(q[i].x,q[i].y);
int tmp=tree.find(q[i].x,q[i].y);
edge[tmp].de=1;
q[i].id=edge[tmp].id;
}
}
//最小生成树加边
sort(edge+1,edge+1+M,cmp3);
int tot=0;
for(int i=1;i<=M;i++)
{
if(!edge[i].de)
{
int u=edge[i].u,v=edge[i].v;
int x=getfather(u),y=getfather(v);
if(x!=y)
{
f[x]=y;
tree.link(u,N+i);
tree.link(v,N+i);
tot++;
if(tot==N-1)break;
}
}
}
for(int i=Q;i>0;i--)
{
if(q[i].type==1)
q[i].ans=tree.key[tree.query(q[i].x,q[i].y)];
else
{
int u=q[i].x,v=q[i].y;
int kind=q[i].id;
int tmp=tree.query(u,v);
if(edge[kind].w<tree.key[tmp])
{
tree.cut(edge[tmp-N].u,tmp);
tree.cut(edge[tmp-N].v,tmp);
tree.link(u,kind+N);
tree.link(v,kind+N);
}
}
}
for(int i=1;i<=Q;i++)
if(q[i].type==1)
printf("%d\n",q[i].ans);
return 0;
}