Connections in Galaxy War

题意:
假设有编号从0开始的n个点,每个点都有一个非负权值p[i]。现在有没有重边的m条边和Q个操作。
对于操作有两种类型
destroy a b 表示摧毁a,b点之间的边
query a 表示从a出发能到的点中,权值比a大权值最大,在权值最大前提下编号最小的点。如果没有这样的点输出-1。

之前做过的一个类似题目:P1197 [JSOI2008]星球大战

题解:
首先说几个这个题目可能出小问题:

1.边是无向边,标记坏边的时候别忘记正反标一下,也就是说她告诉你1-2连边,但是他去毁坏2-1边。
2.注意每个样例之间输出回车!(并且不要关同步流,关闭同步流可能会疯狂PE)

需要的前置只是就是并查集。不会的可以先去学习一下。

读完题目,我们首先会想到每次删去一个点,然后重新建图,算出联通块的个数。然而,这根本不行,时间上过不去,这时就得让我们将头旋转180度,从后往前处理。(也就是说我们先假设边可能会毁掉的边全部毁掉了,然后倒着操作,处理完全部毁掉的情况再把边慢慢的加上,这样时间复杂度就允许了)

怎么从后往前处理呢。
我们先把所有的边给储存起来。
把所有的操作也给储存起来。储存操作的时候不要忘记同时标记毁坏的边是哪些条。
建图,并查集把最后样貌建好(会毁掉的边,一条也不建)。

建图的过程很重要,因为你需要同时维护一个最大值的最小结点。
这里可能就需要你花点心思思考一下了。
并查集你可以理解为好几颗树,树根就是自己的祖先。
每次查询道两个点不在一棵树上的时候,就需要把这两棵树合并起来。

假设要把根节点为1的树合并到根节点为9的树上

int dx=ifind(x);  //dx dy分别为1 和 9  (也就是等价于某个结点的祖父)
int dy=ifind(y);

f[dx]=dy;  //如果不相等的话,这个操作表示把1这颗树连到9这颗树上去

同时数组维护最小值的操作应该维护在dy这个点值上,因为你把dx连到了dy上面。

在这里插入图片描述
最后逆序输出结果即可。

/*Keep on going Never give up*/
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int maxn=10010;
const int mod=998244353;
typedef pair<int,int> pii;
namespace std {
    template <>
    struct hash<pii>{
        size_t operator()(const pii &p0) const {
            return hash<int>()(p0.first) ^ hash<int>()(p0.second);
        }
    };
};
unordered_map<pii,int> mp;

struct node{
    string s;
    int x,y;
}b[maxn];
int a[maxn];
//map<pair<int,int>,int> mp;
vector<pair<int,int> > v;
unordered_map<int,pair<int,int> > mmp;
vector<int> ans;
int f[maxn];
int ifind(int x){
    if(x==f[x]) return x;
    return f[x]=ifind(f[x]);
}
int val[maxn];
int n;
void init(){
    mp.clear();
    mmp.clear();
    ans.clear();
    v.clear();
    for(int i=0;i<=n;i++) f[i]=i;
}

void merg(int x,int y){
    int dx=ifind(x);
    int dy=ifind(y);
    if(dx!=dy){
        f[dx]=dy;
        if(mmp[dx].first>mmp[dy].first){
            mmp[dy]=mmp[dx];
        }
        else if(mmp[dx].first==mmp[dy].first) mmp[dy].second=min(mmp[dx].second,mmp[dy].second);
    }
}

signed main() {
//    ios::sync_with_stdio(false);
//    cin.tie(nullptr);
    int k = 0;
    while(cin>>n){
        init();
        if(k++)puts("");
        for(int i=0;i<n;i++){
            cin>>a[i];
            mmp[i]=make_pair(a[i],i);
        }
        int m;
        cin>>m;
        for(int i=0;i<m;i++){
            int x,y;
            cin>>x>>y;
            v.push_back(make_pair(x,y));
        }
        int q;
        cin>>q;
        for(int i=0;i<q;i++){
            cin>>b[i].s;
            if(b[i].s=="query") cin>>b[i].x;
            else{
                cin>>b[i].x>>b[i].y;
                mp[make_pair(b[i].x,b[i].y)]=1;
                mp[make_pair(b[i].y,b[i].x)]=1;
            }
        }
        for(auto i:v){
            if(mp[make_pair(i.first,i.second)]==1) continue;
            else merg(i.first,i.second);
        }
        for(int i=q-1;i>=0;i--){
            if(b[i].s=="query"){
                int dx=ifind(b[i].x);
                if(mmp[dx].first>a[b[i].x])
                    ans.push_back(mmp[dx].second);
                else ans.push_back(-1);
            }
            else merg(b[i].x,b[i].y);
        }
        int len=ans.size();
        for(int i=len-1;i>=0;i--) cout<<ans[i]<<endl;
    }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值