luogu7月月赛记录

Posted garen-wang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu7月月赛记录相关的知识,希望对你有一定的参考价值。

luogu7月月赛

A

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

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

B

这道题有丶东西

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

但是怎么实现啊?

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

其实没有必要记录数组中的下标(我错在这里),只需要记录这些编号的左右对应关系即可。这样有个好处,就是你可以\(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好写(前提是当你复习过树状数组求逆序对)

我们当然不用枚举出\(\fracn (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

这辈子不可能写压轴题

以上是关于luogu7月月赛记录的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4881 [ Lydsy2017年5月月赛 ] -- 二分图染色+线段树

code+11月月赛

[Lydsy2017年4月月赛]抵制克苏恩

BZOJ5091 摘苹果 BZOJ2017年11月月赛 数学推导 逆元

11月月赛

[Bzoj4832][Lydsy2017年4月月赛]抵制克苏恩 (期望dp)