2017 Multi-University Training Contest

来源:互联网 发布:手机淘宝怎么登陆卖家 编辑:程序博客网 时间:2024/05/16 06:56

弱校联合训练日常被虐惨案。by绍兴一中

hdu 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066

Kanade’s sum

题意:给一个由1-n随机排序组成的序列,每个数只会出现一次,现在,对于每一个子区间,他的权值为出现第k大的数,现在问这个序列总价值为多少。

拿到这题,俩队友拿nth_element和划分数狂套,卡死在区间枚举n * n的复杂度上。。

思路: 对于每一个数,我们可以从小到大枚举,当枚举到第i位数时,记录他左边k+1个比他大的,右边k+1个比他大的。然后左右两两枚举,保证左右组合出来的区间可以使那个数成为第k大的数,然后组合数学计算答案。操作完毕后,将那个数删除,用指针表示即可。那这样子操作的话,每次左右访问只需要访问2 * k次即可以知道能表示所需枚举的左右k大的区间,而全部情况也只需要线性扫描一次即可。

#include <bits/stdc++.h>#define MAXN 500005#define ll long longusing namespace std;int num[MAXN], pre[MAXN], nxt[MAXN], pos[MAXN];int rgt[100], lft[100];int n, k;void remove(int p) {    int tmp1 = pre[p];    int tmp2 = nxt[p];    pre[tmp2] = tmp1;    nxt[tmp1] = tmp2;}ll solve() {    ll ans = 0;    for (int i = 1; i <= n; i++) {        pre[i] = i - 1;        nxt[i] = i + 1;        pos[num[i]] = i;    }    num[0] = num[n + 1] = n + 1;    for (int i = 1; i <= n; i++) {        int p = pos[i];        int l = 0, r = 0;        for (int j = 0, q = p; j < k + 1 && num[q] >= i; j++, q = pre[q]) {            lft[l++] = q;            if (q == 0) {                break;            }        }        for (int j = 0, q = p; j < k + 1 && num[q] >= i; j++, q = nxt[q]) {            rgt[r++] = q;            if (q == n + 1) {                break;            }        }        int b = k - (l - 1) + 1;        int a = l - 2;        while (a >= 0 && b < r) {            ans += 1LL * (lft[a] - lft[a + 1]) * (rgt[b] - rgt[b - 1]) * i;             a--, b++;        }        remove(p);    }    return ans;}int main() {    int T;    scanf("%d", &T);    while (T--) {        scanf("%d %d", &n, &k);        for (int i = 1; i <= n; i++) {            scanf("%d", &num[i]);        }        printf("%lld\n", solve());    }}/*15 21 3 4 5 233*/

RXD and dividing

orz傻逼的读错题,以为是一颗生成树划分成k个不同的集合问每个集合内最短的点相加的值。可能是没认真读过斯坦纳树。

题意:给一个n - 1(不包括1)个节点的集合,你可以划分成k个两两相交为空集的集合, 定义每组的的权值为该组内所有点加编号为1的节点相互连接所经过的边的权值的和。现在问分成k组之后,所有组的和最大是多少。

思路:如果暴力去枚举点划分且用dp计算的话,时间复杂度是不够的。我们可以考虑到,想要他的总和最大,从某个点开始dfs遍历下去,对于每一个点,他最多可以被经过的次数为k次(被访问k个儿子节点),他越被访问得多,总和就越大。
那么只需要从某个点开始(直接从1开始),dfs下去,总和就等于对于每一个点他们min(儿子节点数,k) * 已经走过的权值之和。

#include <bits/stdc++.h>#define MAXN 1000010#define ll long longusing namespace std;map<pair<int, int> , int> mapp;vector<int> vec[MAXN];int size[MAXN];int cost[MAXN];void addEdge(int u, int v, int w) {    mapp[make_pair(u, v)] = w;    mapp[make_pair(v, u)] = w;    vec[u].push_back(v);    vec[v].push_back(u);}void init() {    for (int i = 0; i < MAXN; i++) {        vec[i].clear();    }    memset(size, 0, sizeof(size));    memset(cost, 0, sizeof(cost));    mapp.clear();}void dfs(int u, int fa) {    int len = vec[u].size();    size[u] = 1;    for (int i = 0; i < len; i++) {        int v = vec[u][i];        if (v != fa) {            cost[v] = mapp[make_pair(v, u)];            dfs(v, u);            size[u] += size[v];        }    }}int main() {    int n, k, u, v, w;    while (~scanf("%d %d", &n, &k)) {        init();        for (int i = 0; i < n - 1; i++) {            scanf("%d %d %d", &u, &v, &w);            addEdge(u, v, w);        }        dfs(1, -1);        ll ans = 0;        for (int i = 2; i <= n; i++) {            ans += 1LL * min(size[i], k) * cost[i];        }        printf("%lld\n", ans);    }}

RXD and math

神奇数学题,会发现对于第i个数,若他不是莫比乌斯函数中成0的数,那么他所算出来的值恰好是他自身+后面i * 12+ i * 22 + i * 32 + … + i * i2 + … + f(u(i))能由他自身组成比nk小的莫比乌斯值为0的数的总数。
所以答案就是nk % MOD,快速幂一下即可。n进来的时候记得先MOD一下。
orz没MOD wa了一发被隔壁队超越。

#include <bits/stdc++.h>#define ll long long#define MOD 1000000007#define MAXN 100005using namespace std;bool mub[MAXN];ll ksm(ll a, ll n) {    ll ans = 1, t = a;    while (n) {        if (n & 1) {            ans = (ans * t) % MOD;        }        n >>= 1;        t = (t * t) % MOD;    }    return ans;}int main() {    ll n, m, cas = 1;    while (~scanf("%lld %lld", &n, &m)) {        n %= MOD;        printf("Case #%lld: %lld\n", cas++, ksm(n, m % (MOD - 1)));    }} 

RXD’s date

神奇的签到题,开场半分钟读完,10S码完,吹了1分20S的水看有没有人过题,编译都没编译。。
找小于35的答案即可。orz你们绍兴一中是怕我们被0封所以出了这种签到题嘛

#include <bits/stdc++.h>using namespace std;int main() {    int t, c;    while (~scanf("%d", &t)) {        int ans = 0;        for (int i = 0; i < t; i++) {            scanf("%d", &c);            if (c <= 35) {                ans++;            }        }        printf("%d\n", ans);    }}
原创粉丝点击