2019 ICPC 南昌 (C E G L)

https://vjudge.net/contest/440183#overview

 

当天过了两道,一个签到的简单模拟(L),还有一个最大生成树(E).

 

C - And and Pair

给出长度为1e5的01串,要求满足:

 

 

 的(i,j)有多少对.

数学题,运用排列组合,推导过程里出现了二项式定理.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
#define M 1000000007

string s;

long long ksm(long long b, long long p){
    long long ans = 1 % M;
    while(p){
        if(p & 1) ans = (long long)ans * b % M;
        b = (long long)b * b % M;
        p >>= 1;
    }
    return ans;
}

void solve(){
    cin >> s;
    reverse(s.begin(), s.end());
    long long ct1 = 0, ct0 = 0, ans = 0;
    for(auto i = s.begin(); i != s.end(); i++){
        auto c = *i;
        if(c == '1') {
            ans = (ans + ksm(3, ct1) * ksm(2, ct0)) % M;
            ct1++;
        }else ct0++;
    }
    printf("%lld\n", ans + 1);
}

int main(){
    int t;
    scanf("%d", &t);
    while(t--) solve();   

    return 0;
}
C

 

E - Bob's Problem

最小(大)生成树,这种题目再怎么变好像都不是很难.

可以看出来加黑边没有任何坏处,所以直接全部加上,在此后得到的图上使用Kruskal,首先检验一下在k条白边内能否得到生成树,如果不能则输出-1,否则把树生成出来,然后从大到小把剩下的(如果有)边的值加上去.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;

struct E{
    int from, to, wei, col;
    bool operator<(const E&other) const {return wei > other.wei;}
};
vector<E> e;
queue<int> q;
int n, m, k, fa[50010];

int get(int x) {return fa[x] == x ? x : fa[x] = get(fa[x]);}
void merge(int x, int y) {fa[get(x)] = fa[get(y)];}

void solve(){
    e.clear();
    while(!q.empty()) q.pop();
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; i++) fa[i] = i;
    long long ans = 0;
    while(m--){
        int u, v, w, c;
        scanf("%d%d%d%d", &u, &v, &w, &c);
        if(c == 0) {
            ans += w;
            merge(u, v);
        }else e.push_back({u, v, w, c});
    }
    sort(e.begin(), e.end());

    for(auto i = e.begin();k && i != e.end(); i++){
        auto cur = *i;
        if(get(cur.from) == get(cur.to)) q.push(cur.wei);
        else {
            merge(cur.from, cur.to);
            ans += cur.wei;
            k--;
        }
    }
    while(k-- && !q.empty()){
        ans += q.front();
        q.pop();
    }

    int ct = 0;
    for(int i = 1; i <= n; i++) if(fa[i] == i) ct++;
    if(ct == 1)
        printf("%lld\n", ans);
    else
        puts("-1");
}

int main(){
    int t;
    scanf("%d", &t);
    while(t--) solve();   

    return 0;
}
E

 

G - Eating Plan

 将一个1~n的排列中的每个数转化为其对应的阶乘,得到一个序列.要求从中选择连续的一段,使得该段上的值的和对998857459取模后不小于给定的k(序列不变,对k进行m次询问).

n,m均为1e5.

可以想一想,如果不考虑序列内都是排列的阶乘这一性质,即序列中的数是无规律的,这一问题可能没有合理的时间复杂度的求解方法.

 

答案是,这个模数有猫腻.

如果用下面的方法分解一下998857459的因数:

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
using namespace std;

int t = 998857459;
set<int> s;

int main(){
    for(int i = 2; i < 10000000; i++)
        while(t % i == 0) t /= i, s.insert(i);
    for(auto i : s) cout << i << endl;

    return 0;
}
分解因数

会得到

461 
773 
2803

这意味着对于一个x>=2803,x!=1*2*3*...*461*...*773*...*2803*...*x,其对998857459取模后为0.

因此不管n有多大,最多有2803个值不是0,对于为0的数是很好处理的.

 

这种题目算是用来开眼界吧.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;

struct E{
    int from, to, wei, col;
    bool operator<(const E&other) const {return wei > other.wei;}
};
vector<E> e;
queue<int> q;
int n, m, k, fa[50010];

int get(int x) {return fa[x] == x ? x : fa[x] = get(fa[x]);}
void merge(int x, int y) {fa[get(x)] = fa[get(y)];}

void solve(){
    e.clear();
    while(!q.empty()) q.pop();
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; i++) fa[i] = i;
    long long ans = 0;
    while(m--){
        int u, v, w, c;
        scanf("%d%d%d%d", &u, &v, &w, &c);
        if(c == 0) {
            ans += w;
            merge(u, v);
        }else e.push_back({u, v, w, c});
    }
    sort(e.begin(), e.end());

    for(auto i = e.begin();k && i != e.end(); i++){
        auto cur = *i;
        if(get(cur.from) == get(cur.to)) q.push(cur.wei);
        else {
            merge(cur.from, cur.to);
            ans += cur.wei;
            k--;
        }
    }
    while(k-- && !q.empty()){
        ans += q.front();
        q.pop();
    }

    int ct = 0;
    for(int i = 1; i <= n; i++) if(fa[i] == i) ct++;
    if(ct == 1)
        printf("%lld\n", ans);
    else
        puts("-1");
}

int main(){
    int t;
    scanf("%d", &t);
    while(t--) solve();   

    return 0;
}
E

 

L - Who is the Champion

签到,排个序就好.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int n, G[110][110];
struct T{
    int num, dif, sco;
    bool operator<(const T& other) const {if(sco != other.sco) return sco > other.sco; return dif > other.dif;}
}t[110];

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++){
            scanf("%d", &G[i][j]);
            t[i].dif += G[i][j];
            t[j].dif -= G[i][j];
            t[i].num = i;
        }

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++){
            if(G[i][j] > G[j][i]) t[i].sco += 3;
            else if(G[i][j] == G[j][i]) t[i].sco += 1;
        }

    sort(t + 1, t + 1 + n);
    if(t[1].sco == t[2].sco && t[1].dif == t[2].dif) puts("play-offs");
    else printf("%d\n", t[1].num);

    return 0;
}
L

 

posted @ 2021-06-02 13:39  goverclock  阅读(69)  评论(0编辑  收藏  举报