luogu7月月赛记录

luogu7月月赛

A

借助反作弊系统,一些在月赛有抄袭作弊行为的选手被抓出来了!

这道题直接按照题意写出递归即可。写不出来的退役罢(不是我吧)

B

这道题有丶东西

有脑子的都知道思路:每次从第一位到倒数第二位中找出最大的数字,连带后面的数字一起取出,这样取\(\frac{n}{2}\)次就一定是最大的。

但是怎么实现啊?

因为有频繁的删除,我们不妨使用双向链表。

其实没有必要记录数组中的下标(我错在这里),只需要记录这些编号的左右对应关系即可。这样有个好处,就是你可以\(O(1)\)地访问到那个你要的任意值的龙珠。

初始化下双向链表后,我们从\(n\)枚举到\(1\),如果这个节点后面还有的话就直接用了,接下来就是naive的链表删除环节了。。。

我sbsb在想成询问区间\([1,n-1]\)的最大值并删除两个数,导致想得非常乱还不会。。。

代码很简单:

#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 100005;
int a[maxn], n;
struct Nodes {
    int val, idx;
    Nodes(int val, int idx): val(val), idx(idx){}
    bool operator < (const Nodes &rhs) const {
        return val > rhs.val;
    }
};
std::set<Nodes> s;
int left[maxn], right[maxn], first, last;

int main() {
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        s.insert(Nodes(a[i], i));
        if(i > 1) left[i] = i - 1;
        if(i < n) right[i] = i + 1;
    }
    first = 1; last = n;
    for(int i = 1; i <= n / 2; i++) {
        // get
        int idx = -1;
        std::vector<Nodes> sorry;
        for(std::set<Nodes>::iterator it = s.begin(); it != s.end(); it++) {
            if(it->idx == last) sorry.push_back(*it);
            else {
                idx = it->idx; break;
            }
        }
        for(auto it: sorry) s.insert(it);

        cout << a[idx] << ' ' << a[right[idx]] << ' ';
        
        s.erase(Nodes(a[idx], idx));
        s.erase(Nodes(a[right[idx]], right[idx]));

        if(right[idx] == last) {
            // 右边没有了
            if(left[idx] == 0) break;// 左边也没有了
            else {
                // 左边还有
                last = left[idx];
                right[last] = 0;
            }
        } else {
            // 右边还有
            if(left[idx] == 0) {
                // 左边没有了
                first = right[right[idx]];
                left[first] = 0;
            } else {
                // 左边还有
                right[left[idx]] = right[right[idx]];
            }
        }
    }
    return 0;
}

C

讲道理我觉得比B好写(前提是当你复习过树状数组求逆序对)

我们当然不用枚举出\(\frac{n (n+1)}{2}\)个区间,按照贡献法,设一个逆序对\((a_i,a_j)\),显然它会被算\(i \times (n - j + 1)\)次。

借鉴一下树状数组的思路,它的思路是把数字离散化后按照桶的思想统计数值比它大,还比它早添加的数字个数,因此在树状数组中统一+1。

我们同样是这个思路,枚举前面逆序对的\(a_j\),树状数组加上原来的下标\(i\),每次也是同样的查询,产生的贡献是查询结果乘以\(n -j + 1\)

注意了,离散化是包括去重的。。。应该只有我这种菜鸡忘了。

这道题居然要用__int128。。。

代码:

#include<bits/stdc++.h>
#define ll __int128
const ll maxn = 1000005;
ll a[maxn], b[maxn];
ll n;
struct BIT {
    ll c[maxn];
    inline ll lowbit(int x) {
        return x & -x;
    }
    void add(ll i, ll x) {
        for(; i <= n; i += lowbit(i)) c[i] += x;
    }
    ll query(int i) {
        ll ret = 0;
        for(; i; i -= lowbit(i)) ret += c[i];
        return ret;
    }
} c;
ll read() {
    ll ans = 0, s = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0') {
        if(ch == '-') s = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return s * ans;
}

void print(ll x) {
    if(x >= 10) print(x / 10);
    putchar((x % 10) + '0');
}
int main() {
    ll ans = 0;
    n = read();
    for(ll i = 1; i <= n; i++) {
        b[i] = a[i] = read();
    }
    std::sort(b + 1, b + n + 1);
    ll len = std::unique(b + 1, b + n + 1) - b - 1;
    for(ll i = 1; i <= n; i++) {
        ll pos = std::lower_bound(b + 1, b + len + 1, a[i]) - b;
        c.add(pos, i);
        ll temp = c.query(n) - c.query(pos);
        ans += temp * (n - i + 1ll);
    }
    print(ans);
    putchar('\n');
    return 0;
}

D

这辈子不可能写压轴题

猜你喜欢

转载自www.cnblogs.com/Garen-Wang/p/11273439.html