bzoj3651 网络通信(LCT)

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

4 5 3 5
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

Sold.
Already owned.
Forbidden: monopoly.
Forbidden: redundant.
No such cable.

分析:
我们可以用一种很暴力的方法:
建C棵LCT,每棵的结点个数是n
(n和C的范围都比较小)

用map记录一下每条边属于哪个公司
这就可以解决:Already owned. /No such cable.

记录点的度数,处理:Forbidden: monopoly.

用LCT维护连通性,回答:Forbidden: redundant.

如果冲破了重重考验,我们就可以:Sold.

tip

用map的判断一定要放在最前面
这样就不会影响Forbidden: redundant. 的回答
make_pair的时候一定是:p=make_pair(min(x,y),max(x,y));

一开始一直WA,原因竟是主程序中手残把 - - 写错

程序中,我把所有的结点都放在了“一起”
有一个getbh函数确定每一棵LCT的结点

#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#define pp pair<int,int>

using namespace std;

const int N=800010;
int ch[N][2],pre[N],size[N],du[102][8002];
bool rev[N];
int q[N],m,n,C,T;
pp p1;
map<pp,int> mp;

int getbh(int tree,int bh) {
    return (tree-1)*n+bh;
}

int get(int bh)
{
    return ch[pre[bh]][0]==bh? 0:1;
}

int isroot(int bh)
{
    return ch[pre[bh]][0]!=bh&&ch[pre[bh]][1]!=bh;
}

void update(int bh)
{
    if (!bh) return;
    size[bh]=1;
    if (ch[bh][0]) size[bh]+=size[ch[bh][0]];
    if (ch[bh][1]) size[bh]+=size[ch[bh][1]];
}

void push(int bh)
{
    if (!bh) return;
    if (rev[bh])
    {
        if (ch[bh][0]) rev[ch[bh][0]]^=1;
        if (ch[bh][1]) rev[ch[bh][1]]^=1;
        swap(ch[bh][0],ch[bh][1]);
        rev[bh]^=1;
    }
}

void rotate(int bh)
{
    int fa=pre[bh];
    int grand=pre[fa];
    int wh=get(bh);
    if (!isroot(fa)) ch[grand][ch[grand][0]==fa? 0:1]=bh;
    pre[bh]=grand;
    ch[fa][wh]=ch[bh][wh^1];
    pre[ch[fa][wh]]=fa;
    ch[bh][wh^1]=fa;
    pre[fa]=bh;
    update(fa);
    update(bh);
}

void splay(int bh)
{
    int top=0;
    q[++top]=bh;
    for (int i=bh;!isroot(i);i=pre[i]) q[++top]=pre[i];
    while (top) push(q[top--]);
    for (int fa;!isroot(bh);rotate(bh))
        if (!isroot(fa=pre[bh]))
            rotate(get(bh)==get(fa)? fa:bh);
}

void expose(int bh)
{
    int t=0;
    while (bh)
    {
        splay(bh);
        ch[bh][1]=t;
        update(bh);
        t=bh;
        bh=pre[bh];
    }
}

void makeroot(int bh)
{
    expose(bh);
    splay(bh);
    rev[bh]^=1;
}

void link(int x,int y)
{
    makeroot(x);
    pre[x]=y;
}

void cut(int x,int y)
{
    makeroot(x);
    expose(y); splay(y);
    pre[x]=ch[y][0]=0;
}

int find(int x)
{
    expose(x);
    splay(x);
    while (ch[x][0]) x=ch[x][0];
    return x;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&C,&T);
    int x,y,z;
    mp.clear();
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        pp p;
        p=make_pair(min(x,y),max(x,y));
        mp[p]=z;
        link(getbh(z,x),getbh(z,y));
        du[z][x]++; du[z][y]++;
    }

    for (int i=1;i<=T;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        pp p;
        p=make_pair(min(x,y),max(x,y));
        if (mp[p]==0)
        {
            printf("No such cable.\n");
            continue;
        }
        if (mp[p]==z)
        {
            printf("Already owned.\n");
            continue;
        }
        if (du[z][x]>=2||du[z][y]>=2)
        {
            printf("Forbidden: monopoly.\n");
            continue;
        }
        if (find(getbh(z,x))==find(getbh(z,y)))
        {
            printf("Forbidden: redundant.\n");
            continue;
        }

        printf("Sold.\n");
        int fro=mp[p];
        du[fro][x]--; du[fro][y]--;
        du[z][x]++; du[z][y]++;
        cut(getbh(fro,x),getbh(fro,y));
        link(getbh(z,x),getbh(z,y));
        mp[p]=z;
    }

    return 0;
} 

还有一种写法,没有把所有的结点混在一起(但是比较容易写错)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#define pp pair<int,int>

using namespace std;

const int N=8010;
int ch[102][N][2],pre[102][N],du[102][8002];
bool rev[102][N];
int q[N],m,n,C,T;
pp p1;
map<pp,int> mp;

int get(int tree,int bh)
{
    return ch[tree][ pre[tree][bh] ][0]==bh? 0:1;
}

int isroot(int tree,int bh)
{
    return ch[tree][ pre[tree][bh] ][0]!=bh&&ch[tree][ pre[tree][bh] ][1]!=bh;
}

void push(int tree,int bh)
{
    if (!bh) return;
    if (rev[tree][bh])
    {
        if (ch[tree][bh][0]) rev[tree][ch[tree][bh][0]]^=1;
        if (ch[tree][bh][1]) rev[tree][ch[tree][bh][1]]^=1;
        swap(ch[tree][bh][0],ch[tree][bh][1]);
        rev[tree][bh]^=1;
    }
}

void rotate(int tree,int bh)
{
    int fa=pre[tree][bh];
    int grand=pre[tree][fa];
    int wh=get(tree,bh);
    if (!isroot(tree,fa)) 
        ch[tree][grand][ch[tree][grand][0]==fa? 0:1]=bh;
    pre[tree][bh]=grand;
    ch[tree][fa][wh]=ch[tree][bh][wh^1];
    pre[tree][ch[tree][fa][wh]]=fa;
    ch[tree][bh][wh^1]=fa;
    pre[tree][fa]=bh;
}

void splay(int tree,int bh)
{
    int top=0;
    q[++top]=bh;
    for (int i=bh;!isroot(tree,i);i=pre[tree][i]) q[++top]=pre[tree][i];
    while (top) push(tree,q[top--]);
    for (int fa;!isroot(tree,bh);rotate(tree,bh))
        if (!isroot(tree,fa=pre[tree][bh]))
            rotate(tree,get(tree,bh)==get(tree,fa)? fa:bh);
}

void expose(int tree,int bh)
{
    int t=0;
    while (bh)
    {
        splay(tree,bh);
        ch[tree][bh][1]=t;
        t=bh;
        bh=pre[tree][bh];
    }
}

void makeroot(int tree,int bh)
{
    expose(tree,bh);
    splay(tree,bh);
    rev[tree][bh]^=1;
}

void link(int tree,int x,int y)
{
    makeroot(tree,x);
    pre[tree][x]=y;
}

void cut(int tree,int x,int y)
{
    makeroot(tree,x);
    expose(tree,y); splay(tree,y);
    pre[tree][x]=ch[tree][y][0]=0;
}

int find(int tree,int x)
{
    expose(tree,x);
    splay(tree,x);
    while (ch[tree][x][0]) x=ch[tree][x][0];
    return x;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&C,&T);
    int x,y,z;
    mp.clear();
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        pp p;
        p=make_pair(min(x,y),max(x,y));
        mp[p]=z;
        link(z,x,y);
        du[z][x]++; du[z][y]++;
    }

    for (int i=1;i<=T;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        pp p;
        p=make_pair(min(x,y),max(x,y));
        if (mp[p]==0)
        {
            printf("No such cable.\n");
            continue;
        }
        if (mp[p]==z)
        {
            printf("Already owned.\n");
            continue;
        }
        if (du[z][x]>=2||du[z][y]>=2)
        {
            printf("Forbidden: monopoly.\n");
            continue;
        }
        if (find(z,x)==find(z,y))
        {
            printf("Forbidden: redundant.\n");
            continue;
        }

        printf("Sold.\n");
        int fro=mp[p];
        du[fro][x]--; du[fro][y]--;
        du[z][x]++; du[z][y]++;
        cut(fro,x,y);
        link(z,x,y);
        mp[p]=z;
    }

    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值