第十二届东北师范大学程序设计竞赛正式赛题解

A
可以想到,先手必胜的条件是,存在一个奇数。
所以后手想要必胜就需要把所有奇数+1,使其变成偶数。
统计一下奇数的个数即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
        int n;
        scanf("%d", &n);
        int cnt = 0;
        for(int i = 1; i <= n; i++) {
            int v;
            scanf("%d", &v);
            if(v & 1) cnt++;
        }
        cout<<cnt<<endl;
    }
}

B
首先 O(n2) 的计算出所有点之间的距离。
那么问题就转化成了一个基本的最小生成树问题。

#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#endif // others
//#define file
#define all(x) x.begin(), x.end()
using namespace std;
#define eps 1e-8
const double pi = acos(-1.0);

typedef long long LL;
typedef unsigned long long ULL;
void umax(int &a, int b) {
    a = max(a, b);
}
void umin(int &a, int b) {
    a = min(a, b);
}
int dcmp(double x) {
    return fabs(x) <= eps?0:(x > 0?1:-1);
}
void file() {
    freopen("data_in.txt", "r", stdin);
    freopen("data_out.txt", "w", stdout);
}

namespace solver {
    typedef unsigned long long LL;
    int n;
    struct A {
        double x, y, z;
    };
    struct Edge {
        int u, v;
        double w;
        bool operator < (const Edge & b) const {
            return w < b.w;
        }
    };
    double dis(A a, A b) {
        return sqrt(pow(a.x-b.x, 2) + pow(a.y-b.y, 2) + pow(a.z - b.z, 2));
    }
    int fa[5555];
    void init() {
        for(int i = 0; i < 5555; i++) fa[i] = i;
    }
    int find(int x) {
        return x == fa[x]? x : fa[x] = find(fa[x]);
    }
    void solve() {
        init();
        vector<A> V;
        vector<Edge> E;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            double x, y, z;
            scanf("%lf%lf%lf", &x, &y, &z);
            V.push_back({x, y, z});
        }
        for(int i = 0; i < n; i++)
            for(int j = i + 1; j < n; j++) {
                E.push_back({i+1, j+1, dis(V[i], V[j])});
            }
        sort(all(E));
        double ans = 0;
        for(int i = 0; i < E.size(); i++) {
            int u = E[i].u, v = E[i].v;
            double w = E[i].w;
            int f1 = find(u), f2 = find(v);
            if(f1 == f2) continue;
            fa[f1] = f2;
            ans += w;
        }
        printf("%.2f\n", ans);
    }
}

int main() {
//    file();
    int t;
    scanf("%d", &t);
    while(t--)
        solver::solve();
    return 0;
}

C
模拟题,每次能召唤的时候贪心的把最强的随从尽可能先召唤出来。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;

typedef long long ll;
const int inf = 1<<30;
const int md = 1e9+7;
int T;
int ans;

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>T;
    while(T--)
    {
        ans=0;
        int x;
        priority_queue<int> q;
        int n=30;
        for(int i=1;i<=3;i++)
        {
            cin>>x;
            q.push(x);
            n--;
            if(x==0)ans+=1000;
        }
        int now =0;
        for(int i=1;i<=40;i++)
        {
            if(n)
            {
                cin>>x;
                q.push(x);
                n--;
                if(x==0)ans+=1000;
            }
            ans+=now;
            if(!q.empty())
            {
                now+=q.top();
                q.pop();
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

D
有向图判环问题,拓扑排序了解一下。

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 100000 + 16;
const int MAXM = 500000 + 16;

vector<int> G[MAXN];
int D[MAXN];
void init(int n) {
    for (int i = 0; i <= n; ++i) {
        G[i].clear();
        D[i] = 0;
    }
}

int n, m;
char buf[32];

bool citrus() {
    int cnt = 0;
    queue<int> Q;
    for (int i = 0; i < n; ++i) if (D[i] == 0) Q.push(i);
    while (!Q.empty()) {
        int u = Q.front(); Q.pop(); ++cnt;
        for (auto v : G[u]) if (--D[v] == 0) Q.push(v);
    }
    return cnt == n;
}

int main(int argc, char **argv) {
    while (~scanf("%d%d", &n, &m)) {
        init(n);
        unordered_map<string, int> mp;
        for (int i = 0; i < n; ++i) {
            scanf("%s", buf);
            mp[buf] = i;
        }
        assert(mp.size() == n);
        while (m--) {
            scanf("%s", buf); int u = mp[buf];
            scanf("%s", buf); int v = mp[buf];
            assert(u != v);
            G[v].push_back(u);
            ++D[u];
        }
        if (citrus()) puts("Citrus Saikou!");
        else puts("DameDameYo");
    }
    return 0;
}

E
签到题,模拟下就好了。

#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#endif // others
//#define file
#define all(x) x.begin(), x.end()
using namespace std;
#define eps 1e-8
const double pi = acos(-1.0);

typedef long long LL;
typedef unsigned long long ULL;
void umax(int &a, int b) {
    a = max(a, b);
}
void umin(int &a, int b) {
    a = min(a, b);
}
int dcmp(double x) {
    return fabs(x) <= eps?0:(x > 0?1:-1);
}
void file() {
    freopen("data_in.txt", "r", stdin);
    freopen("data_out.txt", "w", stdout);
}

namespace solver {
    int n;
    void solve() {
        scanf("%d", &n);
        vector<string> res;
        int cnt[3] = {0};
        int p = 0;
        for(int i = 1; i <= n; i++) {
            string tmp;
            cin >> tmp;
            for(int i = 0; i < tmp.size(); i++) {
                if(tmp[i] == 'R') cnt[0]++;
                else if(tmp[i] == 'Y') cnt[1] ++;
                else cnt[2] ++;
            }
        }
        for(int i = 0; i < 3; i++)
            p = max(p, cnt[i]);
        if(p == cnt[0]) res.push_back("Red");
        if(p == cnt[1]) res.push_back("Yellow");
        if(p == cnt[2]) res.push_back("Green");
        sort(all(res));
        for(int i = 0; i < res.size(); i++)
            printf("%s%c", res[i].c_str(), i == res.size()-1?'\n':' ');
    }
}

int main() {
//    file();
    int t;
    scanf("%d", &t);
    while(t--) solver::solve();
    return 0;
}

F
考虑以 dp[i] 代表所有末尾数字为 vi 的方案数。
那么有 dp[i]=i1j=1dp[j]|vj<vi
我们可以用 n2 去实现这个程序,但是显然无法通过。
所以我们可以用数据结构(如线段树,树状数组)去优化这个问题,每次 dp[i] 都是由一个区间求和得到的,这样我们就可以在 O(nlogn) 的时间复杂度解决这个问题。

#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#endif // others
//#define file
#define all(x) x.begin(), x.end()
using namespace std;
#define eps 1e-8
const double pi = acos(-1.0);

typedef long long LL;
typedef unsigned long long ULL;
void umax(int &a, int b) {
    a = max(a, b);
}
void umin(int &a, int b) {
    a = min(a, b);
}
int dcmp(double x) {
    return fabs(x) <= eps?0:(x > 0?1:-1);
}
void file() {
    freopen("data_in.txt", "r", stdin);
    freopen("data_out.txt", "w", stdout);
}

namespace solver {
    LL n;
    const LL mod = 100000007;
    const LL maxn = 110000;
    LL dp[maxn];
    LL C[maxn];
    LL lowbit(LL x) {
        return x & -x;
    }
    void add(LL x, LL v) {
        for(LL i = x; i < maxn; i+=lowbit(i)) {
            C[i] += v;
            C[i] %= mod;
        }
    }
    LL ask(LL x) {
        LL res = 0;
        for(LL i = x; i; i-= lowbit(i))
            res += C[i], res %= mod;
        return res;
    }
    void solve() {
        memset(C, 0, sizeof C);
        LL res = 0;
        scanf("%lld", &n);
        for(LL i = 1; i <= n; i++) {
            LL v;
            scanf("%lld", &v);
            LL ans = ask(v) + 1;
            add(v, ans);
            res += ans;
            res %= mod;
        }
        cout<<res<<endl;
    }
}

int main() {
//    file();
    int t;
    scanf("%d", &t);
    while(t--) solver::solve();
    return 0;
}

G
我们考虑倒着做这个问题,让meopass去寻找huge,这是概率DP的一个常用小技巧,对于DP而言其实正着和倒着推导并没什么区别。
dp[i] 代表meopass刚到区域 i 花费的时间是多少。
考虑状态转移,现在若 P[i][j] 不为0,那么就可以从 dp[j] 转移到 dp[i]
那么对于任意的dp[i]都存在:
dp[i]=(1+dp[j])P[i][j]|P[i][j]!=0
写成方程的形式就是
dp[i]dp[j]P[i][j]|P[i][j]=P[i][j]
因为会存在 dp[i] 可以从 dp[j] 推导, dp[j] 也可以从 dp[i] 推导,所以常规的DP不可行。
对此我们可以对 1 n 的每个i,列出方程
dp[i]dp[j]P[i][j]|P[i][j]=P[i][j]
得到了一个方程组,然后令 dp[1]=0 , 用高斯消元法去解这个方程组即可, 答案就是 dp[m]

#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#endif // others
//#define file
#define all(x) x.begin(), x.end()
using namespace std;
#define eps 1e-8
const double pi = acos(-1.0);

typedef long long LL;
typedef unsigned long long ULL;
void umax(int &a, int b) {
    a = max(a, b);
}
void umin(int &a, int b) {
    a = min(a, b);
}
int dcmp(double x) {
    return fabs(x) <= eps?0:(x > 0?1:-1);
}
void file() {
    freopen("data_in.txt", "r", stdin);
    freopen("data_out.txt", "w", stdout);
}

namespace solver {
    int n, m;
    const int maxn = 111;
    double v[maxn][maxn];
    double a[maxn][maxn];
    int gauss(int n) {
    for(int i = 0 ; i < n ; i++){
            int r = i;
            for(int j = i + 1 ; j < n; j++) if(fabs(a[j][i]) > fabs(a[r][i])) r = j;
    /*浮点搞死小圆中找到最大绝对值的系数来消圆本来是为了优化精度,如果在整数的模意义下,这种行为就没有意义了,此时我们只需要找到第i行及以下任意一个系数非零的元来消即可。
    for(int j = i ; j < n; j++) if(a[j][i]!=0) { r = j; break;}
    */
          if(r != i){
                for(int j = 0 ; j <= n ; j++)
                    swap(a[i][j] , a[r][j]);
            }
            for(int j = n; j >= i; j--)
                for(int k = i + 1; k < n; k++)
                    a[k][j] -= a[k][i]/a[i][i] * a[i][j];
        }//得到上三角矩阵
        for(int i = n - 1 ; i >= 0 ; --i){//回带,消元。
            for(int j = i + 1 ; j < n ; j++)
                a[i][n] -= a[i][j] * a[j][n];
            if(a[i][i] < 1e-10 && a[i][n] > 1e-10) return -1;
            a[i][n] = a[i][n] / a[i][i];
        }
        return 1;
    }
    void solve() {
        scanf("%d%d", &n, &m);
        m --;
        memset(a, 0, sizeof a);
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++)
                scanf("%lf", &v[i][j]);
        }
        for(int i = 1; i < n; i++) {
            for(int j = 0; j < n; j++)
                a[i][j] -= v[i][j];
            a[i][i] = 1;
            a[i][n] = 1;
        }
        a[0][0] = 1;
        a[0][n] = 0;
        gauss(n);
        printf("%.2f\n", a[m][n]);
    }
}

int main() {
//    file();
    int t;
    scanf("%d", &t);
    while(t--) solver::solve();
    return 0;
}

H
这里写图片描述

#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#endif // others
//#define file
#define all(x) x.begin(), x.end()
using namespace std;
#define eps 1e-8
const double pi = acos(-1.0);
const int maxn = 1000000 + 10;
long long int a[maxn];
long long int b[maxn];
long long int sum;
int n;
typedef long long LL;
typedef unsigned long long ULL;
void umax(int &a, int b) {
    a = max(a, b);
}
void umin(int &a, int b) {
    a = min(a, b);
}
int dcmp(double x) {
    return fabs(x) <= eps?0:(x > 0?1:-1);
}
void file() {
    freopen("data_in.txt", "r", stdin);
    freopen("pai_out.txt", "w", stdout);
}

namespace solver {
    void solve() {
        scanf("%d", &n);
        for(int i=0; i<n; i++) scanf("%lld", a+i);
        for(int i=0; i<n-1; i++) b[i] = a[i+1] - a[i];
        sort(b, b+n-1);
        for(int i=1; i<n; i++) a[i] = a[i-1] + b[n-1-i];
        sum = 0;
        for(int i=0; i<n; i++) sum += a[i];
        printf("%lld\n", sum);
    }
};


int main() {
//    file();
    int t;
    scanf("%d", &t);
    while(t--) solver::solve();
    return 0;
}

I
贪心的把出现次数最多的字符的权值尽可能分配大即可。

#include <bits/stdc++.h>
using namespace std;

int arr[27];

bool cmp(int a,int b) {
    return a>b;
}

int main()
{
//    freopen("input.txt", "r", stdin);
   // freopen("output.txt", "w", stdout);
    string x;
    while(cin>>x) {
        memset(arr, 0, sizeof arr);
        int len = x.size();
        for(int i=0; i<len; i++)
            x[i] = tolower(x[i]),arr[x[i]-'a']++;
        sort(arr,arr+26,cmp);
        int p = 26;
        int ans = 0;
        for(int i=0; arr[i]; i++)
            ans += p*arr[i],p--;
        cout<<ans<<endl;
    }
    return 0;
}

J
可以建立线段树对每次询问进行回答。
每个线段树的节点内维护的是一个 55 的矩阵。
对每次询问分成 5 个状态。
0 : 当前子串能组成的目标序列为空
1 : 当前子串能组成的目标序列”2”
2 : 当前子串能组成的目标序列”20”
3 : 当前子串能组成的目标序列”201”
4: 当前子串能组成的目标序列”2018”
v[i][j] 代表从状态i转移到状态j需要删除的字符数。
那么合并两颗线段树节点的时候本质就是在进行状态的转移。
状态的转移详见代码。

#include <bits/stdc++.h>
#define all(x) x.begin(), x.end()

using namespace std;

typedef long long LL;

class Solver {
public:
    static const int maxn = 220000;
    char str[maxn];
    class Seg {
    public:
        int v[6][6];
        Seg operator + (const Seg & b) const {
            Seg tmp;
            for(int i = 0; i < 5; i++)
                for(int j = 0; j < 5; j++)
                    tmp.v[i][j] = maxn;
            for(int i = 0; i < 5; i++)
                for(int j = 0; j < 5; j++)
                    for(int k = 0; k < 5; k++)
                        tmp.v[i][j] = min(tmp.v[i][j], v[i][k] + b.v[k][j]);
            return tmp;
        }
    } tr[maxn << 2];

    void pushup(int rt) {
        tr[rt] = tr[rt<<1] + tr[rt<<1|1];
    }

    void init(Seg & tt) {
        for(int i = 0; i < 5; i++)
            for(int j = 0; j < 5; j++)
                tt.v[i][j] = (i == j? 0 : maxn);
    }

    void show(Seg x) {
        for(int i = 0; i < 5; i++, puts(""))
            for(int j = 0; j < 5; j++)
            printf("%d ", x.v[i][j]);
    }

    void build(int l, int r, int rt) {
        init(tr[rt]);
        if(l == r) {
            if(str[l] == '2') tr[rt].v[0][1] = 0, tr[rt].v[0][0] = 1;
            if(str[l] == '0') tr[rt].v[1][2] = 0, tr[rt].v[1][1] = 1;
            if(str[l] == '1') tr[rt].v[2][3] = 0, tr[rt].v[2][2] = 1;
            if(str[l] == '8') tr[rt].v[3][4] = 0, tr[rt].v[3][3] = 1;
            if(str[l] == '6') tr[rt].v[3][3] = 1, tr[rt].v[4][4] = 1;
            return ;
        }
        int m = (l + r) >> 1;
        if(l <= m) build(l, m, rt<<1);
        if(r > m) build(m+1, r, rt<<1|1);
        pushup(rt);
    }

    int n, q;

    Seg ask(int L, int R, int l, int r, int rt) {
        if(L <= l && R >= r) {
            return tr[rt];
        }
        int m = (l + r) >> 1;
        Seg tmp;
        init(tmp);
        if(L <= m) tmp = tmp + ask(L, R, l, m, rt<<1);
        if(R > m) tmp = tmp + ask(L, R, m+1, r, rt<<1|1);
        return tmp;
    }

    void solve() {
        scanf("%d%d", &n, &q);
        str[0] = 'o';
        scanf("%s", str + 1);
        build(1, n, 1);
        for(int i = 0; i < q; i++) {
            int l, r;
            scanf("%d%d", &l, &r);
            assert(l >= 1 && l <= n);
            assert(r >= 1 && r <= n);
            int ans = ask(l, r, 1, n, 1).v[0][4];
            if(ans >= maxn) puts("-1");
            else printf("%d\n", ans);
        }
    }
} solver;

int main() {
//    freopen("data_in.txt", "r", stdin);
//    freopen("data_out.txt", "w", stdout);
    int t;
    scanf("%d", &t);
    while(t--) solver.solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/meopass/article/details/79953951
今日推荐