2017 Multi-University Training Contest - Team 9

本文详细解析了两道算法题目,一是通过Tarjan算法解决无向图中的可达性问题,二是从混合数组中恢复原始数组的过程及技巧。文章提供了完整的代码实现,并附带了解题思路说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这场打的也不好(貌似也没有打的好的)、

题目好难补QAQ


1005

题意:给出一个无向图,要求任意两点之间至少存在一种方式可达,就是说要么存在u > v 要么存在v > u 要么都存在

思路:可以想象要形成这样的关系最终一定要是一条链,那么我们用tarjan后判断是否是一条链即可

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility>

using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<LL, LL>
#define mst(a, b)    memset(a, b, sizeof a)
#define REP(i, x, n)    for(int i = x; i <= n; ++i)
const int MOD = 1e9 + 7;
const int qq = 1005 + 5;
const int INF = 1e9 + 10;
vector<int> vt[qq], kt[qq];
int n, m, Bcnt, top, Index;
int fa[qq], dfn[qq], low[qq], Stack[qq], belong[qq], indeg[qq];
bool instack[qq];
void Init() {
    mst(dfn, 0);
    mst(fa, -1);
    mst(indeg, 0);
    mst(low, 0);
    mst(belong, 0);
    mst(instack, false);
    Bcnt = top = Index = 0;
    for(int i = 0; i <= n; ++i) {
        vt[i].clear();
        kt[i].clear();
    }
}
int Find(int x) {
    return fa[x] == -1 ? x : fa[x] = Find(fa[x]);
}
void Tarjan(int u) {
    low[u] = dfn[u] = ++Index;
    instack[u] = true;
    Stack[top++] = u;
    int sz = (int)vt[u].size();
    for(int i = 0; i < sz; ++i) {
        int v = vt[u][i];
        if(!dfn[v]) {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if(instack[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    int v;
    if(low[u] == dfn[u]) {
        ++Bcnt;
        do {
            v = Stack[--top];
            instack[v] = false;
            belong[v] = Bcnt;
        } while(v != u) ;
    }
}
void VOA() {
    queue<int> Q;
    for(int i = 1; i <= Bcnt; ++i) {
        if(indeg[i] == 0)    Q.push(i);
    }
    int cnt = 0;
    bool f = true;
    while(!Q.empty()) {
        if(Q.size() > 1) {
            f = false;
            break;
        }
        int u = Q.front();
        Q.pop();
        for(int i = 0; i < (int)kt[u].size(); ++i) {
            int v = kt[u][i];
            indeg[v]--;
            if(indeg[v] == 0)    Q.push(v);
        }
    }
    if(f)    puts("I love you my love and our love save us!");
    else    puts("Light my fire!");
}
int main(){
    int t;    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        Init();
        int a, b;
        for(int i = 0; i < m; ++i) {
            scanf("%d%d", &a, &b);
            vt[a].pb(b);
            int x = Find(a), y = Find(b);
            if(x != y)    fa[y] = x;
        }
        int p = 0;
        for(int i = 1; i <= n; ++i) {
            if(fa[i] == -1)    p++;
        }
        if(p > 1) {
            puts("Light my fire!");
            continue;
        }
        for(int i = 1; i <= n; ++i) {
            if(!dfn[i])    Tarjan(i);
        }
        if(Bcnt == 1) {
            puts("I love you my love and our love save us!");
            continue;
        }
        for(int i = 1; i <= n; ++i) {
            for(int j = 0; j < (int)vt[i].size(); ++j) {
                if(belong[i] == belong[vt[i][j]])    continue;
                indeg[belong[vt[i][j]]]++;
                kt[belong[i]].pb(belong[vt[i][j]]);
            }
        }
        VOA();
    }
    return 0;
}






1008

题意:原本有个数组a, 然后所有(ai + aj)就构成了数组b(1 <= i < j <= n),现在数组a的元素和素数b的元素混到一起了求出原来的数组a

思路:排序之后最前面两个一定出现在数组a中,然后用一个mp维护一定出现在数组a中的元素构成的数组b中的元素、

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility>

using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<LL, LL>
#define mst(a, b)    memset(a, b, sizeof a)
#define REP(i, x, n)    for(int i = x; i <= n; ++i)
const int MOD = 1e9 + 7;
const int qq = 125250 + 5;
const int INF = 1e9 + 10;
int num[qq], ans[qq];
map<int, int> mp;


int main(){
    int n;
    while(scanf("%d", &n) != EOF) {
        mp.clear();
        for(int i = 0; i < n; ++i) {
            scanf("%d", num + i);
        }
        if(n == 0) {
            printf("0\n");
            continue;
        } else if(n == 1) {
            printf("1\n%d\n", num[0]);
            continue;
        }
        sort(num, num + n);
        int cnt = 0;
        ans[cnt++] = num[0], ans[cnt++] = num[1];
        mp[ans[0] + ans[1]]++;
        for(int i = 2; i < n; ++i) {
            if(mp[num[i]]) {
                mp[num[i]]--;
            } else {
                ans[cnt++] = num[i];
                for(int i = 0; i < cnt - 1; ++i) {
                    mp[ans[cnt - 1] + ans[i]]++;
                }
            }
        }
        printf("%d\n", cnt);
        for(int i = 0; i < cnt; ++i) {
            printf("%d%c", ans[i], i == cnt - 1 ? '\n' : ' ');
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值