2021杭电多校9

G Boring data structure problem

题意

有一个队列和4种操作:
L,把下个数插到队列的左边
R,把下个数插到队列的右边
G x,把x从队列里删除
Q,查询该队列最中间的数(设该队列有m个数,最中间即 ⌈ m + 1 2 ⌉ \lceil \frac{m+1}{2}\rceil 2m+1

思路

用数组模拟该队列,用类似链表的方式去存储数的上一个位置和下一个位置,并且实时记录最中间的数的下标。
添加元素的时候:
若队列的长度为奇数:
⌈ 3 + 1 2 ⌉ = 2 \lceil \frac{3+1}{2}\rceil=2 23+1=2, ⌈ 4 + 1 2 ⌉ = 3 \lceil \frac{4+1}{2}\rceil=3 24+1=3
如果数添加到左边,则中间的数不变(中间的数+1,但是数是添加到左边的,所以不用动)
如果数添加到右边,则中间的数向右移一位(中间的数+1,数是添加到右边的,所以需要右移一位)
若队列的长度是偶数:
⌈ 4 + 1 2 ⌉ = 3 \lceil \frac{4+1}{2}\rceil=3 24+1=3, ⌈ 5 + 1 2 ⌉ = 3 \lceil \frac{5+1}{2}\rceil=3 25+1=3
如果数添加到左边,则中间的数向左移一位(中间数不变,左边添加数,需要左移)
如果数添加到右边,则中间的数不变(中间数不败你,右边添加数,不用动)
删除元素的时候:
若队列的长度为奇数:
如果删除的数在中间数或者中间数的左边,则中间数向右移一位
如果删除的数在中间数的右年,则中间数不变
若队列的长度为偶数:
如果删除的数在中间数或者中间数的右边,则中间数向左移一位
如果删除的数在中间数的左边,则中间数不变
用两个数组表示前驱和后继,删除数的时候,进行变换,next[pre[x]]=next[x],pre[next[x]]=pre[x]

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-18 13:40:11
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-20 17:07:41
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.18\G.cpp
 */
#include <bits/stdc++.h>
using namespace std;
const int N = 2e7 + 5;
const int MAXN = 1e7 + 5;
int n, cnt = 0, l, r, ans = 1, q, x;
int a[N]; //模拟队列
int c[MAXN]; //记录数在a数组的位置
int Pre[N], Next[N];
int main()
{
    l = 1e7;
    r = 1e7 + 1;
    q = l + 1;
    for (int i = 1; i < N; i++)
        Next[i] = i + 1;
    for (int i = 1; i < N; i++)
        Pre[i] = i - 1;
    char ch;
    scanf("%d", &n);
    while (n--) {
        getchar();
        scanf("%c", &ch);
        switch (ch) {
        case 'L':
            a[l] = ans;
            c[ans] = l;
            l--;
            ans++;
            if (cnt % 2 == 0)
                q = Pre[q];
            //cout<<q<<endl;
            cnt++;
            break;
        case 'R':
            a[r] = ans;
            c[ans] = r;
            r++;
            ans++;
            if (cnt % 2)
                q = Next[q];
            cnt++;
            break;
        case 'G':
            scanf("%d", &x);
            Next[Pre[c[x]]] = Next[c[x]];
            Pre[Next[c[x]]] = Pre[c[x]];
            if (cnt % 2) {
                if (c[x] <= q)
                    q = Next[q];
            } else {
                if (c[x] >= q)
                    q = Pre[q];
            }
            cnt--;
            break;
        case 'Q':
            printf("%d\n", a[q]);
        }
    }
    return 0;
}

H Integers Have Friends 2.0

题意

给出一些数。如果两个数对于同一个数的模相同,则这两个数在同一个集合中,求集合最多有几个数。

思路

首先,当m=2时,集合被分为奇数和偶数,一般为 ⌈ n 2 ⌉ \lceil \frac{n}{2}\rceil 2n
选择两个位置,这两个数的位置均在答案中的概率至少为 1 4 \frac{1}{4} 41,反之为 3 4 \frac{3}{4} 43
假如重复取30次,则为0.0017858
可以看作0
所以方法是可靠的
选定了两个位置p1,p2后,m可以取diff=abs(a[p1]-a[p2])的素因子
通过枚举diff的因子当m进行操作,并一直max

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-20 07:27:24
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-20 07:40:49
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.18\H.cpp
 */
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 3e6 + 5;
ll n, a[maxn];
ll cal(ll x, ll y)
{
    ll ret = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i] % x == y)
            ret++;
    }
    return ret;
}
int main()
{
    srand(time(NULL));
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%lld", &a[i]);
        ll ans = 1;
        for (int i = 1; i <= 30; i++) {
            int p1 = rand() % n + 1;
            int p2 = rand() % n + 1;
            while (p1 == p2) {
                p1 = rand() % n + 1;
                p2 = rand() % n + 1;
            }
            ll diff = abs(a[p1] - a[p2]);
            for (ll i = 2; 1ll * i * i <= diff; i++) {
                if (diff % i == 0) {
                    while (diff % i == 0)
                        diff /= i;
                    ans = max(ans, cal(i, a[p1] % i));
                }
            }
            if (diff > 1)
                ans = max(ans, cal(diff, a[p1] % diff));
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值