Codeforces Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2)

题目链接:https://codeforces.com/contest/1553
1553E.Permutation Shift
题目大意:是否能在转置\(k\)轮和交换\(m\)次的条件下将\((1,2,3,...,n)\)变成\((a_{1},a_{2},a_{3},...,a_{n})\)\(0\leq k<n,0\leq m\leq \frac{n}{3}\),转置指元素后移\(k\)位,交换指两个元素交换位置

题目思路:
先将每个数都减\(1\)
转置0轮\((0,1,2,3)\)
转置1轮\((3,0,1,2)\)
转置2轮\((2,3,0,1)\)
转置3轮\((1,2,3,0)\)
不难发现转置k轮实际上就是\(p[i] = i-k(mod \ n)\)
对于每一个转置我们可以求出对a序列来说不动点的个数
a序列为\((1,2,0,3)\)
转置0轮\((0,1,2,3)\),不动点数\(cnt[0] = 1\)
转置1轮\((3,0,1,2)\),不动点数\(cnt[1] = 0\)
转置2轮\((2,3,0,1)\),不动点数\(cnt[2] = 1\)
转置3轮\((1,2,3,0)\),不动点数\(cnt[3] = 2\)
不动点即\(a[i] =p[i] = i-k(mod \ n)\),得 \(k = i-a[i] (mod \ n)\),因\(++cnt[k]\)即可
剩下都是需要移动的点,交换\(m\)次最多可导致\(2m\)个元素交换,因此可以判断如果\(cnt[k]+2m<n\)当前转置就不满足条件,跳过即可
因为\(m\leq \frac{n}{3}\),最多可以交换\(\frac{2n}{3}\)个元素,剩下\(\frac{n}{3}\)是不动点的个数,又因为\(\sum cnt[k] = n\),最多只有3个\(k\)是满足条件的,直接暴力即可
暴力求出环数\(num\),需要移动的点即为\(n-num\),对于原序列和a序列每个\(i\)都会引条边指向\(a[i]\),即\(i\rightarrow a[i]\),而转置k轮又会导致\(i- k\rightarrow a[i]\),即\(i\rightarrow a[i] + k\),遍历边即可

AC代码:

#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <stack>
#include <deque>
#include <queue>
#include <cmath>
#include <map>
#include <set>
using namespace std;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
//typedef __int128 int128;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int N = 3e5 + 10, M = 4e7 + 10;
const int base = 1e9;
const int P = 131;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
int a[N], cnt[N], ans[5];
int pos;
bool vis[N];
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		pos = 0;
		int n, m;
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; ++i)
		{
			scanf("%d", &a[i]);
			--a[i];
			++cnt[(i - a[i] + n) % n];
		}
		for (int k = 0; k < n; ++k)
		{
			if (cnt[k] + 2 * m < n)
				continue;
			for (int i = 0; i < n; ++i)
				vis[i] = false;
			int num = 0;
			for (int i = 0; i < n; ++i)
			{
				if (vis[i])
					continue;
				for (int j = i; !vis[j]; j = (a[j] + k) % n)
					vis[j] = true;
				++num;
			}
			if (n - num <= m)
				ans[++pos] = k;
		}
		printf("%d ", pos);
		for (int i = 1; i <= pos; ++i)
			printf("%d ", ans[i]);
		printf("\n");
	}
	return 0;
}

1553F.Pairwise Modulo
题目大意:\(p_k = \sum_{1 \le i, j \le k} a_i \bmod a_j\)

题目思路:\(a_i\)的前缀和为\(s_i\),每增加一个\(a_{i}\),答案加

\[\sum_{j=1}^{i-1}a_{j} \ mod \ a_{i}+a_{i} \ mod \ a_{j}\\ =\sum_{j=1}^{i-1}a_{j}-\left \lfloor \frac{a_{j}}{a_{i}} \right \rfloor a_{i} + \sum_{j=1}^{i-1}a_{i}-\left \lfloor \frac{a_{i}}{a_{j}} \right \rfloor a_{j}\\ =s_{i-1}+(i-1)a_{i}- a_{i}\sum_{j=1}^{i-1}\left \lfloor \frac{a_{j}}{a_{i}} \right \rfloor-\sum_{j=1}^{i-1}\left \lfloor \frac{a_{i}}{a_{j}} \right \rfloor a_{j} \]

对于\(a_{i}\sum_{j=1}^{i-1}\left \lfloor \frac{a_{j}}{a_{i}} \right \rfloor\),区间查询\([da_i,(d+1)a_i)\)的个数,此区间每个数的贡献为\(d\),然后对\(a_j\)进行单点插入个数1
对于\(\sum_{j=1}^{i-1}\left \lfloor \frac{a_{i}}{a_{j}} \right \rfloor a_{j}\),单点查询\(a_i\)对前面所有的\(a_j\)的贡献,然后枚举\(a_j\)的倍数\(da_j\),对\(da_j\)进行单点增加\(a_j\),说的可能不清楚,那我上图!

AC代码:

#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <stack>
#include <deque>
#include <queue>
#include <cmath>
#include <map>
#include <set>
using namespace std;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
//typedef __int128 int128;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int N = 3e5 + 10, M = 4e7 + 10;
const int base = 1e9;
const int P = 131;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
int n;
ll a[N], s[N], ai[N], aj[N];
int lowbit(int x)
{
	return x & -x;
}
void add(ll tr[], int x, ll c)
{
	for (int i = x; i < N; i += lowbit(i))
		tr[i] += c;
}
ll sum(ll tr[], int x)
{
	ll res = 0;
	for (int i = x; i; i -= lowbit(i))
		res += tr[i];
	return res;
}
ll getai(ll x)
{
	ll res = 0;
	for (ll d = 1; d * x <= N; ++d)
	{
		ll cnt = sum(ai, min((ll)N, (d + 1) * x) - 1) - sum(ai, d * x - 1);
		res += cnt * d;
	}
	return x * res;
}
ll getaj(ll x)
{
	return sum(aj, x);
}
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i]);
		s[i] = s[i - 1] + a[i];
	}
	ll ans = 0;
	for (int i = 1; i <= n; ++i)
	{
		ans += s[i - 1] + (i - 1) * a[i] - getai(a[i]) - getaj(a[i]);
		printf("%lld ", ans);
		add(ai, a[i], 1);
		for (ll d = 1; d * a[i] <= N; ++d)
			add(aj, d * a[i], a[i]);
	}
	return 0;
}

posted @ 2021-07-28 16:25  xiaopangpang7  阅读(64)  评论(0编辑  收藏  举报