您的位置:首页 > 其它

ZOJ 3261 Connections in GalaxyWar(并查集:离线处理)

2014-03-13 13:58 465 查看
ZOJ 3261 Connections in GalaxyWar(并查集:离线处理)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3261


题意:
有n个点和m条边,每个点带有一个权值p[i]。现在给出Q条命令,要你输出对应的答案。命令格式如下:

query u :该命令需要输出当前与u点相连的点编号x,x要满足p[x]是所有与u相连的点中最大的 且 p[x]>p[u]。如果有多个满足条件的x存在,那么就输出编号最小的那个x点的编号。

destroy u v:该命令将删除u与v的边。(保证在执行该命令前u与v之间有一条边)
(假设输入的m条边不重复)

分析:
每个点看成并查集中的一个点,那么query u的时候就是查找u所属的连通分量中p[]值最大的点编号。所以我们可以始终让一个连通分量的根就是那个p[]值最大的点 且 在合并连通分量的时候依然维持这一性质。
由于并查集只能增加边,而题目的命令却只有删除边,所以自然想到将所有命令先预读入内存,然后从最后一条命令往前,一条一条添加边处理。
具体处理过程如下:
首先我们读入M条边,但是此时先不连接任何边。接着我们读入每条命令,并且将会被删除的边标记出来。然后我们将那些没有被删除的边都用并查集连接起来。

然后我们逆序处理每条命令(从最后一条命令开始),如果是query命令就是返回该分量的根编号(还需要判断根的p值是否>u的p值)。如果是destory u v 命令,其实就是添加u v边的命令(不是删除边了,想想为什么)。

注意:如果根不是自己但是根的能量和自己一样,照样不能作为求援对象.
AC代码(新):200ms
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
const int maxn=10000+5;

int p[maxn];//power值

//并查集
int fa[maxn];
int findset(int x)
{
    return fa[x]==-1? x:fa[x]=findset(fa[x]);
}
int bind(int u,int v)
{
    int fu=findset(u);
    int fv=findset(v);
    if(fu != fv)
    {
        if(p[fu]>p[fv] || (p[fu]==p[fv] && fu<fv) )//fu是根
        {
            fa[fv]=fu;
        }
        else//fv是根
        {
            fa[fu]=fv;
        }
        return 1;
    }
    return 0;
}

//边
struct Edge
{
    int u,v;
    Edge(){}
    Edge(int u,int v):u(u),v(v){}

    bool operator<(const Edge &rhs)const
    {
        return u<rhs.u || (u==rhs.u && v<rhs.v);
    }
}edges[20000+5];
bool vis[20000+5];

//命令
struct command
{
    int type;
    int v;
}coms[50000+5];

int main()
{
    int n,m,Q;
    bool first=true;
    while(scanf("%d",&n)==1)
    {
        if(!first) printf("\n");
        first=false;

        for(int i=0;i<n;i++)
            scanf("%d",&p[i]);

        scanf("%d",&m);
        map<Edge,int> mp;//边与边的编号的映射
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            if(u>v) swap(u,v);
            edges[i]=Edge(u,v);//可优化
            mp[edges[i]]=i;
        }

        memset(vis,0,sizeof(vis));//vis[i]==true表第i条边已被删除

        scanf("%d",&Q);
        for(int i=0;i<Q;i++)
        {
            char str[100];
            int u,v;

            scanf("%s",str);
            if(str[0]=='q')
            {
                scanf("%d",&u);

                coms[i].type=0;
                coms[i].v=u;
            }
            else if(str[0]=='d')
            {
                scanf("%d%d",&u,&v);
                if(u>v) swap(u,v);
                int id=mp[Edge(u,v)];//获取对应边的编号

                vis[id]=1;//删除此边
                coms[i].type = 1;
                coms[i].v=id;
            }
        }

        //连通所有未被删除的边
        memset(fa,-1,sizeof(fa));
        for(int i=0;i<m;i++)if(vis[i]==false)
        {
            bind(edges[i].u,edges[i].v);
        }

        //逆序处理所有命令并将query结果保存在vc中
        vector<int> vc;
        for(int i=Q-1;i>=0;i--)
        {
            if(coms[i].type == 0)//query命令
            {
                int root = findset(coms[i].v);
                if(p[root]>p[coms[i].v])
                    vc.push_back(root);
                else
                    vc.push_back(-1);
            }
            else//destroy命令
            {
                int u=edges[coms[i].v].u, v=edges[coms[i].v].v;
                bind(u,v);
            }
        }

        for(int i=vc.size()-1;i>=0;i--)
            printf("%d\n",vc[i]);
    }
    return 0;
}

AC代码:160ms
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int MAXN=10000+100;
int F[MAXN];
int v[MAXN];
//int val[MAXN];//最大值的下标
//int num[MAXN];//最大值
int findset(int a)
{
    if(F[a]==-1)return a;
    return F[a]=findset(F[a]);
}
void bind(int i,int j)//保持能量大的作为根,能量相同则编号小的做根
{
    int fa=findset(i);
    int fb=findset(j);
    if(fa!=fb)
    {
         if(v[fa]==v[fb])//能量相同
        {
            if(fa<fb)//编号小的做根
            {
                F[fb]=fa;
            }
            else if(fa>fb)
            {
                F[fa]=fb;
            }
        }
        else if(v[fa] != v[fb])//能量不同
        {
            if(v[fa]<v[fb])//fb能量大,做根
            {
                F[fa]=fb;
            }
            else if(v[fa]>v[fb])
            {
                F[fb]=fa;
            }
        }
    }
}
struct command
{
    int type;//0查询,1销毁
    int u,v;
}coms[50000+100];
struct edge
{
    int u,v;

}edges[20000+100];
bool vis[20000+100];
map<int,int>mp[MAXN];
int main()
{
    int n,m;
    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",&v[i]);
            //val[i]=i;
            //num[i]=v[i];
            mp[i].clear();
        }

        scanf("%d",&m);//读边数
        for(int i=0;i<m;i++)
        {
            vis[i]=true;//该边可行
            int u,v;
            scanf("%d%d",&u,&v);
            if(u>v)swap(u,v);
            edges[i].u=u;
            edges[i].v=v;
            mp[u][v]=i;
        }
        int Q;
        scanf("%d",&Q);
        for(int i=0;i<Q;i++)
        {
            char str[20];
            scanf("%s",str);
            if(str[0]=='q')
            {
                coms[i].type=0;
                scanf("%d",&coms[i].u);
            }
            else if(str[0]=='d')
            {
                coms[i].type=1;
                int u,v;
                scanf("%d%d",&u,&v);
                if(u>v)swap(u,v);
                coms[i].u=u;
                coms[i].v=v;
                vis[ mp[u][v] ]=false;
            }
        }
        for(int i=0;i<m;i++)if(vis[i])
        {
            int u=edges[i].u,v=edges[i].v;
            bind(u,v);
        }
        int tot=0;
        int ans[50000+100];
        for(int i=Q-1;i>=0;i--)
        {
            if(coms[i].type==0)
            {
                int fa=findset(coms[i].u);
                if(fa!=coms[i].u&&v[fa]>v[coms[i].u])//这里一定要记住加上后面的判断,如果根不是自己但是根的能量和自己一样,照样不能作为求援对象
                    ans[tot]=fa;
                else
                    ans[tot]=-1;
                tot++;
            }
            else if(coms[i].type==1)
            {
                bind(coms[i].u,coms[i].v);
            }
        }
        for(int i=tot-1;i>=0;i--)
            printf("%d\n",ans[i]);
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: