3651: 网络通信
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 83 Solved: 58
[ Submit][ Status][ Discuss]
Description
有一个由M 条电缆连接的 N 个站点组成的网络。为了防止垄断,由 C 个公司控制所有的电缆,规定任何公司不能控制连接同一个站点的两条以上的电缆(可以控制两条)。同时规定,每个公司不能有多余的电缆,所谓的多余,是指属于同一个公司的电缆不能形成环。
在运作过程中,不同公司之间会进行电缆买卖。请你写一个程序判断买卖是否合法。
Input
输入第一行有4个由空格隔开的整数 N,M,C和 T。N(1≤N≤ 8 000)表示站点数,M(0≤M≤100 000)表示连接站点的电缆数。C(1≤C≤ 100)表表示公司数量,T 表示电缆买卖次
数。后面有M行,每行三个整数Sj1,Sj2和Kj,表示连接站点Sj1和Sj2(1≤Sj1< Sj2 ≤ n)的电缆属于Kj(1≤Kj≤C)公司拥有,任意两个站点只有一条直接相连的电缆,输入状态合法。最后T(0≤T≤100 000)行,每行三个整数 Si1, Si2和 Ki,表示 Ki公司想购买站点Si1和Si2之间的电缆。
Output
输出共 T行,表示处理的结果,有以下几种可能的结果:
1、“No such cable.” 两个站点间没有电缆。
2、 “Already owned.” 电缆己经是 Ki 公司控制。
3、 “Forbidden: monopoly.” Ki 公司己经控制了两条连接 Si1 或 Si2 的电缆。
4、 “Forbidden: redundant.” Ki 公司控制的线路会出现环。
5、 “Sold.” 可以买卖。
Sample Input
1 2 1
2 3 1
3 4 2
1 4 2
1 3 3
1 2 3
1 2 3
1 4 3
2 3 3
2 4 3
Sample Output
Already owned.
Forbidden: monopoly.
Forbidden: redundant.
No such cable.
HINT
Source
题解:这道题是学长出的互测题的第二题,被学长贴标签为EASY&&LCT裸题。当然我这么傻逼,当时还不会LCT,只能打打并查集,骗了60分。。。。。
啊啊啊!!终于会LCT 了,于是想要水掉这道题,结果昨天晚上查错,先是用近一个小时找到了一个手残错误,然后过了样例,结果自己出的第一组小样例,就被卡了 T_T
最后发现问题出在判断Already owned.上,然后傻逼的我询问fye学姐,如何判断x,y 直接有直连边啊,学姐云:为什么要在lct 中判断,你直接map一下不就好了。 我石化。。。
然后就改成了这个样子。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
#define C 120
#define N 8003
using namespace std;
map<int,int> mp;
int n,m,c,t;
int fa[C][N],col[N][C],ch[C][N][3],rev[C][N],top,st[N];
int next[C][N];
int isroot(int x,int k)
{
return ch[k][fa[k][x]][1]!=x&&ch[k][fa[k][x]][0]!=x;
}
int get(int x,int k)
{
return ch[k][fa[k][x]][1]==x;
}
void pushdown(int x,int k)
{
if (rev[k][x]&&x)
{
swap(ch[k][x][0],ch[k][x][1]);
if (ch[k][x][0]) rev[k][ch[k][x][0]]^=1;
if (ch[k][x][1]) rev[k][ch[k][x][1]]^=1;
rev[k][x]=0;
}
}
void rotate(int x,int k)
{
int y=fa[k][x];int z=fa[k][y]; int which=get(x,k);
if (!isroot(y,k)) ch[k][z][ch[k][z][1]==y]=x;
ch[k][y][which]=ch[k][x][which^1]; fa[k][ch[k][y][which]]=y;
ch[k][x][which^1]=y; fa[k][y]=x; fa[k][x]=z;
}
void splay(int x,int k)
{
top=0; st[++top]=x;
for (int i=x;!isroot(i,k);i=fa[k][i])
st[++top]=fa[k][i];
for (int i=top;i>0;i--) pushdown(st[i],k);
while (!isroot(x,k))
{
int y=fa[k][x];
if (!isroot(y,k)) rotate(get(x,k)==get(y,k)?y:x,k);
rotate(x,k);
}
}
void access(int x,int k)
{
int t=0;
while (x)
{
splay(x,k);
ch[k][x][1]=t;
t=x; x=fa[k][x];
}
}
void rever(int x,int k)
{
access(x,k); splay(x,k); rev[k][x]^=1;
}
void link(int k,int x,int y)
{
rever(x,k); fa[k][x]=y; splay(x,k);
}
int find(int x,int k)
{
access(x,k);
splay(x,k);
while (ch[k][x][0]) x=ch[k][x][0];
return x;
}
void cut(int x,int y,int k)
{
rever(x,k); access(y,k); splay(y,k); ch[k][y][0]=fa[k][x]=0;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&c,&t);
mp.clear();
for (int i=1;i<=m;i++)
{
int x,y,z; scanf("%d%d%d",&x,&y,&z);
if (x>y) swap(x,y);
link(z,x,y); col[x][z]++; col[y][z]++;
int a=(x-1)*8000+y; mp[a]=z;//map 映射,因为一共不超过8000个点,所以 <span style="font-family: Arial, Helvetica, sans-serif;">(x-1)*8000+y 相当于对点对进行编号,然后就可以在之后通过映射查询边的信息了,长知识啊</span>
}
for (int i=1;i<=t;i++)
{
int x,y,z; scanf("%d%d%d",&x,&y,&z);
if (x>y) swap(x,y);
bool p=false;
int a=(x-1)*8000+y;
if (mp[a])
{
if (mp[a]==z) { printf("Already owned.\n"); continue; }
if (col[x][z]>=2||col[y][z]>=2)//col 数字用来计算一个点不同颜色的出度
{
printf("Forbidden: monopoly.\n");
continue;
}
if (find(x,z)==find(y,z))
{
printf("Forbidden: redundant.\n");
continue;
}
cut(x,y,mp[a]); col[x][mp[a]]--; col[y][mp[a]]--;
link(z,x,y); col[x][z]++; col[y][z]++; mp[a]=z;
printf("Sold.\n");
}
else
{ printf("No such cable.\n"); continue; }
}
}