2017 Multi-University Training Contest - Team 3

弱校联合训练日常被虐惨案。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 long

using 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());
    }
}

/*
1
5 2
1 3 4 5 2

33

*/

##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 long

using 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 * 1 2 {^2} 2+ i * 2 2 {^2} 2 + i * 3 2 {^2} 2 + … + i * ⌊ i ⌋ 2 {\lfloor\sqrt i\rfloor^2} i 2 + … + f(u(i))能由他自身组成比 n k {n^k} nk小的莫比乌斯值为0的数的总数。
所以答案就是 n k {n^k} nk % MOD,快速幂一下即可。n进来的时候记得先MOD一下。
orz没MOD wa了一发被隔壁队超越。

#include <bits/stdc++.h>

#define ll long long
#define MOD 1000000007
#define MAXN 100005

using 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值