AtCoder Grand Contest 001

A BBQ Easy

题目大意:
史努克在参加一个BBQ派对, 他要准备N组食物, 他有2N的食材, 需要两两组成一个食物, 食物的价值是两食材中较小的那个。 问最大总价值是多少

解题思路:
将其从小到大排序之后,计算奇数位置的和即可

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int a[maxn];
int n;
int main() {
	cin >> n;
  	n <<= 1;
  	for (int i = 1; i <= n; i++)
      	cin >> a[i];
  	sort (a + 1, a + n + 1);
  	int res = 0;
  	for (int i = 1; i <= n; i++) {
      if (i & 1)
        	res += a[i];
    }
  	cout << res << endl;
}
B - Mysterious Light

题目大意:
高桥くん有一个边长为N的三枚镜子构成的正三角形, 顶点为a, b, c。他有一个超级步枪,放在AB段的P点上,使得AP=X。并沿着平行于BC的方向发射一道光。

光以直线传播,以镜子的形式反射,但是有一个特殊的地方:它会被自己的轨迹反射,当光回到步枪的时候,光被吸收。
光的路径总长度是多少?

解题思路:
每次反射可以看成是在一个平行四边形内折射,所以直接递归处理即可
递归终止条件:恰好能够折射到一个端点在这里插入图片描述
左图是折射之后仍然要进行递归,可以发现递归之后仍是平行四边形
右图是这射之后不需要进行递归了,可以直接返回

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
ll N, X, ans;
void dfs(ll len1, ll len2) { //op递归种类,len1步长,len2要走的长度
    ll num = len2 / len1;
    ans += (2 * num - 1) * len1;
    if (len2 % len1 == 0) return;//终止条件
    ans += len1;
    dfs(len2 % len1, len1);
}
int main() {
    cin >> N >> X;
    ans += N;
    dfs(min(X, N - X), max(X, N - X));
    cout << ans << endl;
}
C - Shorten Diameter

题目大意:
经过最少的删点操作,使树的直径不超过k,并输出最少操作数目

解题思路:
若k是偶数,则遍历每个点为根节点,统计超过深度k/2的个数有多少,取最小即可
若k是奇数,则遍历每一条边,然后分别遍历边两端的节点,统计超过深度k/2的个数有多少即可

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 10;
int n, k;
vector <int> G[maxn];
int res = 0, ans = 0, tmp1 = 0, tmp2 = 0;
void dfs(int u, int f, int d) {
    for (auto v : G[u]) {
        if (v == f) continue;
        dfs(v, u, d + 1);
    }
    if (d > k / 2) res++;
}
int main() {
    int u, v;
    cin >> n >> k;
    for (int i = 1; i < n; i++) {
        cin >> u >> v;
        G[u].push_back(v), G[v].push_back(u);
    }
    ans = n + 1;
    if (!(k & 1))
        for (int i = 1; i <= n; i++) {
            res = tmp1 = tmp2 = 0;
            dfs(i, 0, 0);
            ans = min(ans, res);
        }
    else {
        for (int i = 1; i <= n; i++) {
            for (auto v : G[i]) {
                res = 0;
                dfs(v, i, 0);
                dfs(i, v, 0);
                ans = min(ans, res);
            }
        }
    }
    cout << ans << endl;
}
D - Arrays and Palindrome

题目大意:
已知一个重排之后的a序列,询问b序列

  • b序列全为正整数,且之和等于a的序列之后
  • 要求存在序列满足,前 a 1 a_1 a1个字符为回文串,随后 a 2 a_2 a2个字符为回文串…
  • b 1 b_1 b1个字符为回文串,随后 b 2 b_2 b2个字符为回文串…
  • 那么该序列全为相同的字符

解题思路:
白嫖我队友的思路以及图片,队友博客
在这里插入图片描述

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxm = 1e5 + 10;
const int maxn = 1e5 + 10;
int n, m, cnt, len;
int a[maxm], b[maxn];
int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) 
        cin >> a[i];
    int num = 0, k1 = 0, k2 = 0;
    for (int i = 1; i <= m; i++)
        if (a[i] & 1) 
            num++, k2 = k1, k1 = i;
    if (m == 1) {
        if (a[1] == 1) cout << a[1] << endl << 1 << endl << a[1] << endl;
        else cout << a[1] << endl << 2 << endl << n - 1 << " " << 1 << endl;
        return 0;
    }
    if (num > 2) {
        cout << "Impossible" << endl;
        return 0;
    }
    else if (num == 2)//将奇数个放在头尾
        swap(a[k1], a[1]), swap(a[k2], a[m]);
    else if (num == 1)
        swap(a[k1], a[1]);
    for (int i = 1; i <= m; i++)
        cout << a[i] << " ";
    cout << endl;
    if (a[m] == 1) cout << m - 1 << endl;
    else cout << m << endl;
    cout << a[1] + 1 << " ";
    for (int i = 2; i < m; i++)
        cout << a[i] << " ";
    if (a[m] - 1 != 0) cout << a[m] - 1 << endl;
    else cout << endl;
}
E BBQ Hard

题目大意:
n n n个数,求 ∑ i = 1 j = n ∑ j = i + 1 n C a i + b i + a j + b j a i + a j \sum_{i=1}^{j=n}\sum_{j=i+1}^{n}C_{a_i+b_i+a_j+b_j}^{a_i+a_j} i=1j=nj=i+1nCai+bi+aj+bjai+aj的和,对1e9+7取模

解题思路:
众所周知: C x + y x C_{x+y}^x Cx+yx代表 ( 0 , 0 ) (0,0) (0,0) ( x , y ) (x,y) (x,y)的方案数(然而我并不知道
所以: C a i + b i + a j + b j a i + a j C_{a_i+b_i+a_j+b_j}^{a_i+a_j} Cai+bi+aj+bjai+aj代表 ( − a i , − a j ) (-a_i,-a_j) (ai,aj) ( b i , b j ) (b_i,b_j) (bi,bj)的方案数
然后dp即可,最后去除掉 ( − a i , − a i ) (-a_i,-a_i) (ai,ai) ( b i , b i ) (b_i,b_i) (bi,bi)的方案数,再除2即可

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + 10;
const ll maxm = 4e3 + 100;
const ll maxv = 2e3 + 10;
const ll mod = 1e9 + 7;
ll dp[maxm][maxm], ans;
ll a[maxn], b[maxn];
ll fac[maxm << 1];
ll qpow(ll a, ll b) {
    ll res = 1;
    a %= mod;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
void init() {
    fac[1] = 1;
    for (ll i = 2; i < 2 * maxm; i++) 
        fac[i] = fac[i - 1] * i % mod;
}
ll C(ll n, ll m) {
    return fac[n] * qpow(fac[n - m], mod - 2) % mod * qpow(fac[m], mod - 2) % mod;
}
ll N;
int main() {
    init();
    cin >> N;
    for (ll i = 1; i <= N; i++) {
        cin >> a[i] >> b[i];
        dp[maxv - a[i]][maxv - b[i]]++;
    }
    for (ll i = 1; i <= 2 * maxv; i++)
        for (ll j = 1; j <= 2 * maxv; j++) {
            dp[i][j] = (dp[i][j] + dp[i - 1][j] + dp[i][j - 1]) % mod;
        }
    for (ll i = 1;  i <= N; i++) {
        ans = (ans + dp[maxv + a[i]][maxv + b[i]]) % mod;
        ans = (ans - C(2 * (a[i] + b[i]), 2 * a[i]) + mod) % mod;
    }
    ans = ans * qpow(2, mod - 2) % mod;
    cout << ans << endl;
}
F - Wide Swap

题目大意:
给一个元素集合为 { 1 , 2 , . . . , N } ( 1 ≤ N ≤ 5 e 5 ) \{1,2,...,N\}(1\le N \le 5e5) {1,2,...,N}(1N5e5)的排列P,当有 i i i, j j j ( 1 ≤ i < j ≤ N ) (1\le i < j \le N) (1i<jN)满足 j − i ≥ K j-i \ge K jiK ( 1 ≤ K ≤ N − 1 ) (1\le K \le N-1) (1KN1) ∣ P i − P j ∣ = = 1 |P_i-P_j|==1 PiPj==1,时可以交换 P i P_i Pi P j P_j Pj
求:可能排列中字典序最小的排列

解题思路:
有个非常妙的思路,就是将下标和值进行对换,关于新的下标相邻大于等于K即可交换,求出最后的序列即为答案
首先证明一个定理:
对于 ∣ i − j ∣ < K |i-j|<K ij<K, i < j i<j i<j, p i < p j p_i<p_j pi<pj则进行交换之后得到的最终序列 p i ′ < p j ′ p_i^{'}<p_j^{'} pi<pj(p是原数组)
经过对换之后, i i i始终在 j j j前,因为对应的下标为 p i ′ p_i^{'} pi p j ′ p_j^{'} pj而下标中 p i ′ < p j ′ p_i^{'}<p_j^{'} pi<pj
证毕
然后具体思想是看这篇博客的,就不再赘述了

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
const int inf = 0x3f3f3f3f;
int n, k;
int p[maxn], mx[maxn << 2], ans[maxn];
#define ls rt << 1
#define rs rt << 1 | 1
void pushup(int rt) {
    mx[rt] = p[mx[ls]] > p[mx[rs]] ? mx[ls] : mx[rs];
}
void build(int lc, int rc, int rt) {//线段树存储的是区间最大值的下标
    if (lc == rc) {
        mx[rt] = lc;
        return;
    }
    int mi = (lc + rc) >> 1;
    build(lc, mi, ls);
    build(mi + 1, rc, rs);
    pushup(rt);
    return;
}
void del(int lc, int rc, int rt, int x) {
    if (lc == rc) {
        mx[rt] = 0;//删除该值产生影响
        return;
    }
    int mi = (lc + rc) >> 1;
    if (x <= mi) del(lc, mi, ls, x);
    else del(mi + 1, rc, rs, x);
    pushup(rt);
    return;
}
int query(int lc, int rc, int rt, int L, int R) {
    if (R < lc || rc < L) return 0;
    if (L <= lc && rc <= R) return mx[rt];
    int mi = (lc + rc) >> 1;
    int u = query(lc, mi, ls, L, R), v = query(mi + 1, rc, rs, L, R);
    return p[u] > p[v] ? u : v;
}
int inq[maxn];
priority_queue <int> que;//默认从大到小
void check(int id) {
    if (inq[id]) return;
    if (query(1, n, 1, id - k + 1, id + k - 1) == id)
    //因为是反过来遍历,所以如果当前所在区间id是最大的,那么就说明入度为0
        que.push(id), inq[id] = 1;
}
int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> p[i];
    p[0] = -inf;
    build(1, n, 1);
    for (int i = 1; i <= n; i++) check(i);
    for (int i = n; i >= 1; i--) {
        int u = que.top(); que.pop();
        ans[u] = i;
        //即[u-k+1,u+k-1]内没有比p[u]大的,处理相对位置关系(又是大根堆,大的位置先出来,为了最小)所以从后往前赋值
        del(1, n, 1, u);
        int pos;
        //就跟拓扑排序过程一摸一样,检查删除之后有无入度为0的点
        if ((pos = query(1, n, 1, u - k + 1, u - 1))) check(pos);
        if ((pos = query(1, n, 1, u + 1, u + k - 1))) check(pos);
    }
    for (int i = 1; i <= n; i++)
        cout << ans[i] << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值