ZOJ 3261 /哈理工OJ 1913 Connection in War(逆向并查集)(STL应用)

 
Connection in War
Time Limit: 2000 MS Memory Limit: 32768 K
Total Submit: 98(15 users) Total Accepted: 29(14 users) Rating:  Special Judge: No
Description

在一个未知名的次元里,有很多岛国。他们正处于类似三国时期的阶段,为了得到绝对的强权将进行一场大战。有一些小国很害怕战争,他们就切断了和大国相连的桥。但是跟小国a相连的小国b可能和大国c相连,这样小国a就危险了。每一个国家都有一个权值,用来表示它的强大程度。现在国王们只会干两件事:

① 命令工匠拆掉和相邻的某一个国家连接的桥。

② 问一下丞相和自己国家直接或间接相连的国家里哪个国家的权值最大。


Input

有多组测试样例,每组测试样例第一行有一个n(1 <= n < 10000),表示有n个国家(国家编号从0开始),第二行有n个值,表示每个国家的权值。

第三行有一个m,表示这些国家之间连着m条桥。

接下来m行每行两个数a b,表示a国和b国之间有一条桥相连。

接下来有一个数p(1<= p <= 50000),表示有p件事。

接下来有p行,每行有两种格式:

① destroy a b,表示把a国和b国相连的桥去掉

② query a,表示询问和a国直接或者间接相连的最大权值国家的编号


Output

当问及和a国相连的国家中的权值最大的国家的编号,

输出国家的编号然后换行。如果a国已经是最强大的国家,即没有别的国家的权值比a国的权值大,则输出-1换行.

每两组数据中间有一个换行。


Sample Input

2

20 30

1

0 1

5

query 1

query 0

destroy 0 1

query 0

query 1


Sample Output

-1

1

-1

-1




分步详解:(如果不喜欢map可以看最后的代码)

我们知道,一个东西建造起来很容易,但是摧毁就很费劲了,我们两个并查集和是很难分开的。(如果加入了路径压缩的成分就更不用说了)而且我们这里有摧毁前的query操作,当然也有摧毁后的query的操作,摧毁一样东西是很难实现的,但是我们可以这样理解一个问题:摧毁后,相当于没有建造。

也就是所谓的:逆向思维。

我们反向考虑问题,例如题干中的样例,我们可以倒序操作,把:

query 1

query 0

destroy 0 1

query 0

query 1

变成:

query 1

query 0

merge 0 1(这里是我定义的merge)(相当于join)反正知道是建造路就行了。

query 0

query 1

这样来理解,这个摧毁的问题就迎刃而解了。这里有一道相似的思路的题目是杭电(hdu)的D-CITY。题号是4496。思路是一样的,逆向思维。

这里强调完了逆向思维,我们来分步骤操作:

首先是输入和操作:

        int t;//有t组桥,表示想通
        scanf("%d",&t);
        for(int i=0;i<t;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x>y)//这里为了控制map对应统一所用。
            {
                swap(x,y);
            }
            ff[i].x=x;
            ff[i].y=y;
        }
        int m;//表示有m个操作
        scanf("%d",&m);
        for(int i=0;i<m;i++)
        {
            char s[10];
            scanf("%s",s);
            if(s[0]=='q')//如果是查询操作,我们这里只输入一个x表示要查询的点,y标记上-1,表示这个是查询操作
            {
                scanf("%d",&ed[i].x);
                ed[i].y=-1;
            }
            if(s[0]=='d')//如果是摧毁操作,我们这里对应的桥的内容标记上1,一会在建桥的时候我们这里摧毁了的先不要建(逆向思维)等摧毁这个桥之后的Q操作完毕之后再建上。
            {
                int x,y;
                scanf("%d%d",&x,&y);
                if(x>y)//同上
                {
                    swap(x,y);
                }
                ed[i].x=x;
                ed[i].y=y;
                pair<int,int >pal(ed[i].x,ed[i].y);
                mm[pal]=1;
            }
        }

然后是事前建桥(这里边也有没有被摧毁的桥,我们要先建造上)

        for(int i=0;i<t;i++)
        {
            pair<int,int >temp(ff[i].x,ff[i].y);
            if(mm[temp]!=1)//如果这个桥没有摧毁标记,我们直接建上
            {
                merge(ff[i].x,ff[i].y);
            }
        }
然后是逆向处理:

        int cont=0;
        for(int i=m-1;i>=0;i--)
        {
            if(ed[i].y==-1)//如果有查询标记
            {
                if(ed[i].x==find(ed[i].x))//并且他找到的最大权值的编号是自己
                {
                    ans[cont++]=-1;//输出-1
                }
                else
                {
                    ans[cont++]=find(ed[i].x);//否则代表能找到比他权值更大的编号的点。
                }
            }
            else//相反,如果没有查询标记,说明我们要重新建这个桥了(逆向思维)
            {
                merge(ed[i].x,ed[i].y);
            }
        }
那么,如何控制找到的最大权值编号呢?我们这里在merge操作上加点小技巧就行了:


int find(int x)
{
    return f[x]==x?x:find(f[x]);
}
void merge(int x,int y)
{
    int temp1=find(x);
    int temp2=find(y);
    if(a[temp1]>a[temp2])//这里a数组表示权值数组。//这里应用的小技巧就能保证权值问题了。
    {
        f[temp2]=temp1;
    }
    if(a[temp1]<a[temp2])
    {
        f[temp1]=temp2;
    }
}
最后上完整的AC代码:(可以对应上边的分步详解理解)

/*逆向思维:当然先要建造没有摧毁的路,然后再往上补被摧毁的路。*/

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
int a[10005],f[10005],ans[50005];
struct node
{
    int x,y;
}ff[50005],ed[50005];
int find(int x)
{
    return f[x]==x?x:find(f[x]);
}
void merge(int x,int y)
{
    int temp1=find(x);
    int temp2=find(y);
    if(a[temp1]>a[temp2])
    {
        f[temp2]=temp1;
    }
    if(a[temp1]<a[temp2])
    {
        f[temp1]=temp2;
    }
}
int main()
{
    int ok=1;
    int n;
    while(~scanf("%d",&n))
    {
        if(ok!=1)//奇葩输出格式
        printf("\n");
        ok++;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=0;i<n;i++)
        {
            f[i]=i;
        }
        map<pair<int ,int >,int >mm;
        int t;
        scanf("%d",&t);
        for(int i=0;i<t;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x>y)
            {
                swap(x,y);
            }
            ff[i].x=x;
            ff[i].y=y;
        }
        int m;
        scanf("%d",&m);
        for(int i=0;i<m;i++)
        {
            char s[10];
            scanf("%s",s);
            if(s[0]=='q')
            {
                scanf("%d",&ed[i].x);
                ed[i].y=-1;
            }
            if(s[0]=='d')
            {
                int x,y;
                scanf("%d%d",&x,&y);
                if(x>y)
                {
                    swap(x,y);
                }
                ed[i].x=x;
                ed[i].y=y;
                pair<int,int >pal(ed[i].x,ed[i].y);
                mm[pal]=1;
            }
        }
        for(int i=0;i<t;i++)
        {
            pair<int,int >temp(ff[i].x,ff[i].y);
            if(mm[temp]!=1)
            {
                merge(ff[i].x,ff[i].y);
            }
        }
        int cont=0;
        for(int i=m-1;i>=0;i--)
        {
            if(ed[i].y==-1)
            {
                if(ed[i].x==find(ed[i].x))
                {
                    ans[cont++]=-1;
                }
                else
                {
                    ans[cont++]=find(ed[i].x);
                }
            }
            else
            {
                merge(ed[i].x,ed[i].y);
            }
        }
        for(int i=cont-1;i>=0;i--)
        {
            printf("%d\n",ans[i]);
        }
    }
}
这里再上一份大牛的AC代码(没有用map的)
转自:http://www.cnblogs.com/kuangbin/archive/2013/04/05/3001160.html
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <map>
using namespace std;

const int MAXN=10010;
int F[MAXN];
int p[MAXN];
int val[MAXN];//最大值的下标
int num[MAXN];//最大值
int find(int x)
{
    if(F[x]==-1)return x;
    return F[x]=find(F[x]);
}
void bing(int u,int v)
{
    int t1=find(u),t2=find(v);
    if(t1!=t2)
    {
        F[t1]=t2;
        if(num[t1]>num[t2])
        {
            num[t2]=num[t1];
            val[t2]=val[t1];
        }
        else if(num[t1]==num[t2] && val[t2]>val[t1])
            val[t2]=val[t1];
    }
}
map<int,int>mp[MAXN];
struct Edge
{
    int u,v;
}edge[20010];
bool used[20010];
struct Node
{
    int op;
    int u,v;
}node[50010];
int ans[50010];
char str[20];
int main()
{
    int n;
    int Q;
    int m;
    int u,v;
    bool first=true;
    while(scanf("%d",&n)==1)
    {
        if(first)first=false;
        else printf("\n");
        memset(F,-1,sizeof(F));
        for(int i=0;i<n;i++)
        {
            scanf("%d",&p[i]);
            val[i]=i;
            num[i]=p[i];
            mp[i].clear();
        }

        scanf("%d",&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&u,&v);
            if(u>v)swap(u,v);
            mp[u][v]=i;
            edge[i].u=u;
            edge[i].v=v;
            used[i]=false;
        }
        scanf("%d",&Q);
        for(int i=0;i<Q;i++)
        {
            scanf("%s",&str);
            if(str[0]=='q')
            {
                node[i].op=0;
                scanf("%d",&node[i].u);
            }
            else
            {
                node[i].op=1;
                scanf("%d%d",&u,&v);
                if(u>v)swap(u,v);
                node[i].u=u;
                node[i].v=v;
                int tmp=mp[u][v];
                used[tmp]=true;
            }
        }
        for(int i=0;i<m;i++)
          if(!used[i])
          {
              bing(edge[i].u,edge[i].v);
          }
        int cnt=0;
        for(int i=Q-1;i>=0;i--)
        {
            if(node[i].op==0)
            {
                u=node[i].u;
                int t1=find(u);
                if(num[t1]>p[u])ans[cnt++]=val[t1];
                else ans[cnt++]=-1;
            }
            else
            {
                bing(node[i].u,node[i].v);
            }
        }
        for(int i=cnt-1;i>=0;i--)printf("%d\n",ans[i]);
    }
    return 0;
}










你可能感兴趣的:(ZOJ,3261,逆向并查集,3261)