The 10th Jimei University Programming Contest

文章讨论了几个编程竞赛中的问题,包括A+B问题的枚举算法、使用Dijkstra算法求解最短路径,染色问题的组合计数,时间复杂度分析,以及并查集在兄弟校问题中的应用。
摘要由CSDN通过智能技术生成

A. A+B问题

由于数据范围较小,所以直接从小到大枚举可能的 X X X进制,注意判断 X X X要大于题目中出现的最大值,若遇到符合题意的值就退出循环,时间复杂度 O ( 300 T ) O(300T) O(300T)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 2e6 + 10;
const int INF = 1e16;
const int mod = 1e9 + 7;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    string x, y, z;
    cin >> x >> y >> z;
    for(int i = 2;i <= 300;i++)
    {
        int ans = 0;
        int t1 = 0, t2 = 0, t3 = 0;
        int p;
        for(auto c : x)
        {
            if(c >= '0' && c <= '9')
            p = c - '0';
            else
            p = c - 'A' + 10;
            ans = max(ans, p);
            t1 = t1 * i + p;
        }
        for(auto c : y)
        {
            if(c >= '0' && c <= '9')
            p = c - '0';
            else
            p = c - 'A' + 10;
            ans = max(ans, p);
            t2 = t2 * i + p;
        }
        for(auto c : z)
        {
            if(c >= '0' && c <= '9')
            p = c - '0';
            else
            p = c - 'A' + 10;
            ans = max(ans, p);
            t3 = t3 * i + p;
        }
        if(t1 + t2 == t3 && ans < i)
        {
            cout << i << '\n';
            return;
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

B. 看比赛

由于每次走的路都是选择最快的路,所以每一步一定都在某条最短路上

所以先用 d i j k s t r a dijkstra dijkstra正反都跑一遍最短路,如果 d 1 [ v ] + d 2 [ u ] + w = d 1 [ n ] d1[v] + d2[u] + w = d1[n] d1[v]+d2[u]+w=d1[n],说明 v v v u u u之间的路一定是最短路上的某一段,保留,通过这样的方法,我们可以删除除最短路外的其它路径,且重新构造的图为没有环的有向图

然后看如何取胜,如果某人已经到达了节点 u u u而且 u u u n n n相连,对这个人来说 u u u节点是他的必胜节点,所以我们从终点往起点走,如果 1 1 1节点有出现过 M M M的必胜态,则 M M M胜利,不然 I I I胜利,由于必胜态的相邻节点是必输态,所以用拓扑排序就能解决问题,时间复杂度 O ( n l o g m ) O(nlogm) O(nlogm)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const double eps = 1e-9;
const int N = 2e6 + 10;
const int INF = 1e16;
const int mod = 1e9 + 7;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    int n, m;
    cin >> n >> m;
    vector<array<int, 3>> edge;
    vector<vector<pii>> e(n + 5);
    for(int i = 1;i <= m;i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        edge.push_back({u, v, w});
        e[u].push_back({v, w});
        e[v].push_back({u, w});
    }
    auto dij = [&](int x) {
        vi dis(n + 1, INF);
        vi vis(n + 1, 0);

        dis[x] = 0ll;
        priority_queue<pii, vector<pii>, greater<pii>> q;
        q.emplace(0, x);
        while (!q.empty()) {
            auto [d, u] = q.top();
            q.pop();
            if (vis[u]) continue;
            vis[u] = 1;
            for (auto [v, w]: e[u]) {
                if (dis[v] <= d + w)continue;
                dis[v] = d + w;
                q.emplace(dis[v], v);
            }
        }
        return dis;
    };
    vector<int> d1, d2;
    d1 = dij(1);
    d2 = dij(n);
    vector<vector<int>> ne(n + 5);
    vector<int> cnt(n + 5), f(n + 5);
    for(auto [u, v, w] : edge)
    {
        if(d1[v] + w + d2[u] == d1[n])
        {
            ne[u].push_back(v);
            cnt[v]++;
        }
        if(d1[u] + w + d2[v] == d1[n])
        {
            ne[v].push_back(u);
            cnt[u]++;
        }
    }
    queue<int> q;
    q.push(n);
    while(!q.empty())
    {
        int fr = q.front();
        q.pop();
        for(auto u : ne[fr])
        {
            if(f[fr] == 0)
            f[u] = 1;
            cnt[u]--;
            if(cnt[u] == 0)
            q.push(u);
        }
    }
    if(f[1] == 1)
    cout << "Little M is the winner." << '\n';
    else
    cout << "Little I is the winner." << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

C. 方格染色

不是我说,这第一眼也太像状压 d p dp dp了(

由于任意两个黑色方格均不相邻,把一列看成整体,最后的答案中列的情况只有三种:白白,白黑,黑白

首先,我们把相邻列有黑块的列看作整体,例如:黑白,白黑,黑白或者白黑,黑白,白黑等等,剩下的列均为白白

黑块共有 k k k个,所以它能被分成 d ( 1 ≤ d ≤ m i n ( k , n − k + 1 ) ) d(1 \leq d \leq min(k,n-k+1)) d(1dmin(k,nk+1))个整体,对于某个 d d d,其实就是在 k − 1 k - 1 k1个空位中选择 d − 1 d-1 d1个空位插入隔板,所以共有 C ( k − 1 , d − 1 ) C(k-1,d-1) C(k1,d1)

分为一个个整体之后,再考虑剩下的白白列,由于每个黑色列整体不能相邻(若相邻的话那就是一个整体),所以剩下要考虑的就是把一个个整体和白白列放在一起,要求每个黑块整体不相邻。白白列共有 n − k n - k nk个,所以就是在 n − k + 1 n - k + 1 nk+1个空位中选 d d d个空位放黑块整体,共有 C ( n − k + 1 , d ) C(n - k + 1, d) C(nk+1,d)

由于每个黑块整体都有两种状态,如长度为 3 3 3的黑块有黑白,白黑,黑白和白黑,黑白,白黑状态,所以 d d d个整体有 2 d 2^d 2d种情况

所以答案为 ∑ d = 1 m i n ( k , n − k + 1 ) C ( k − 1 , d − 1 ) ∗ C ( n − k + 1 , d ) ∗ 2 d \sum_{d = 1}^{min(k, n - k + 1)}C(k - 1, d - 1) * C(n - k + 1, d) * 2^d d=1min(k,nk+1)C(k1,d1)C(nk+1,d)2d,注意答案取 m o d mod mod k = 0 k = 0 k=0 n < k n<k n<k的特判,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const double eps = 1e-9;
const int N = 2e6 + 10;
const int INF = 1e16;
const int mod = 998244353;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int fac[N], infac[N];
ll C(ll n,ll m){//n个数选m个数
  return fac[n] * infac[n - m] % mod * infac[m] % mod;
}
inline ll binpow(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % mod;
        a = (ll)a * a % mod;
        b >>= 1;
    }
    return res;
}
void solve()
{
    int n, k;
    cin >> n >> k;
    if(k == 0)
    {
        cout << "1" << '\n';
        return;
    }
    if(n < k)
    {
        cout << "0" << '\n';
        return;
    }
    int ans = 0;
    for(int i = 1;i <= min(k, n - k + 1);i++) // 分几块
    {
        int t1 = C(k - 1, i - 1); // 把若干个黑色方格分成i块的分法
        int t2 = C(n - k + 1, i); // 把i块分开
        ans = (ans + t1 * t2 % mod * binpow(2, i) % mod) % mod;
    }
    cout << ans << '\n';
}
signed main()
{
    fac[0] = 1;
    infac[0] = 1;
    for (int i = 1; i <= 1e6;i++){
        fac[i] = i * fac[i - 1] % mod;
        infac[i] = binpow(fac[i], mod - 2);
}
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

E. 时间超限α

遍历每个评测机,小 M M M的代码在每个评测机 i i i的最大花费时间为 a [ i ] [ m i n ( k i , m ) ] a[i][min(k_i,m)] a[i][min(ki,m)],最后的答案就是所有的评测机最大花费时间的最大值,时间复杂度 O ( n ∑ k i ) O(n\sum{k_i}) O(nki)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 2e6 + 10;
const int INF = 1e16;
const int mod = 1e9 + 7;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int a[2005][10005];
void solve()
{
    int n;
    cin >> n;
    for(int i = 1;i <= n;i++)
    {
        cin >> a[i][1];
        for(int j = 1;j <= a[i][1];j++)
        cin >> a[i][j + 1];
    }
    int q;
    cin >> q;
    int ans = -1;
    for(int i = 1;i <= n;i++)
    {
        if(q <= a[i][1])
        ans = max(ans, a[i][q + 1]);
        else
        ans = max(ans, a[i][a[i][1] + 1]);
    }
    cout << ans << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
    solve();
    return 0;
}

I. 期中考试

从起始日开始循环到结束日,如果当天不用加训则计数,时间复杂度 O ( 7000 ) O(7000) O(7000)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 2e6 + 10;
const int INF = 1e16;
const int mod = 1e9 + 7;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    int a, b, c;
    cin >> a >> b >> c;
    int t1, x, t2, y;
    cin >> t1 >> x >> t2 >> y;
    int cnt = 0;
    while(1)
    {
        if(x != a && x != b && x != c)
        cnt++;
        x++;
        if(x == 8)
        {
            x = 1;
            t1++;
        }
        if(t1 == t2 && x == y)
        break;
    }
    cout << cnt << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
    solve();
    return 0;
}

L. 兄弟校问题

先把所有城市相同的学校放入并查集,由于题目中提到,关键词要完全一致地出现,所以可以把学校名通过下划线分成前部分关键词和后部分关键词。由于不区分大小写,所以统一将大写字母转成小写字母放入 s e t set set,每次询问关键词时,把两个都含有该关键词的学校放入并查集,时间复杂度 O ( 50 n m ) O(50nm) O(50nm)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const double eps = 1e-9;
const int N = 2e6 + 10;
const int INF = 1e16;
const int mod = 1e9 + 7;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
struct dsu {
    vi fa;
    dsu(int n = 1){
        fa = vi( n+1 , -1ll );
        fa[0] = 0;
    }

    int getfa(int x) {
        if (fa[x] < 0) return x;
        return fa[x] = getfa(fa[x]);
    }

    void merge(int x, int y) {
        x = getfa(x), y = getfa(y);
        if (x == y) return;
        if (fa[x] > fa[y]) swap(x, y);
        fa[x] += fa[y], fa[y] = x;
    }

    int size(int x) {
        x = getfa(x);
        return -fa[x];
    }
};
string ct[1005];
vector<set<string>> un(1005);
void solve()
{
    int n, m;
    cin >> n >> m;
    dsu d(n);
    for(int i = 1;i <= n;i++)
    {
        string t;
        cin >> t >> ct[i];
        t += '_';
        string now = "";
        for(auto c : t)
        {
            if(c >= 'A' && c <= 'Z')
            now += c - 'A' + 'a';
            else if(c == '_')
            {
                //cout << now << '\n';
                un[i].insert(now);
                now = "";
            }
            else
            now += c;
        }
    }
    for(int i = 1;i <= n;i++)
    {
        for(int j = i + 1;j <= n;j++)
        {
            if(ct[i] == ct[j])
            d.merge(i, j);
        }
    }
    while(m--)
    {
        string qq;
        cin >> qq;
        string q;
        for(auto c : qq)
        {
            if(c >= 'A' && c <= 'Z')
            q += c - 'A' + 'a';
            else
            q += c;
        }
        for(int i = 1;i <= n;i++)
        {
            if(un[i].count(q))
            {
                for(int j = i + 1;j <= n;j++)
                {
                    if(un[j].count(q))
                    d.merge(i, j);
                }
                break;
            }
        }
    }
    for(int i = 1;i <= n;i++)
    cout << d.size(i) - 1 << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
    solve();
    return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值