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;
}