Codeforces Round #842 (Div. 2) A-E

A - Greatest Convex

题目链接:Problem - A - Codeforces

解题思路:x! + (x-1)! = (x-1)! (x + 1),所以当x == k - 1时,必然可以整除。 

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)|1
typedef long long ll;
const int mx = 2e5 + 10;
const int mod = 1e9 + 7;


 
int main() {
	int t, n;
	scanf("%d", &t);
	while (t--) {
		scanf("%d", &n);
		printf("%d\n", n-1);
	}

	return 0;
}

B - Quick Sort

题目链接:Problem - B - Codeforces

解题思路:计算1、2、3....的递增序列长度,这一个不用动,其他的都放到后面即可。

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)|1
typedef long long ll;
const int mx = 2e5 + 10;
const int mod = 1e9 + 7;

int a[mx];
int pos[mx];

int main() {
	int t, n, k;
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d", &n, &k);
		for (int i=1;i<=n;i++) {
			scanf("%d", a+i);
			pos[a[i]] = i;
		}
		int l = 1;
		while (l < n && pos[l] < pos[l+1]) {
			l++;
		}
		if (l == n)
			printf("0\n");
		else
			printf("%d\n", 1 + (n - l - 1) / k);
	}

	return 0;
}

C - Elemental Decompress

题目链接:Problem - C - Codeforces

解题思路:从给定的序列中可以得出肯定a、b两个序列有一个位置的值和它相同,所以先放到a,如果a已经有了就放到b,如果都有了那么就是无解了。然后就继续贪心,一个位置的另一个序列的值就尽量取最大,也就是x<=a[i]就行。

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)|1
typedef long long ll;
const int mx = 2e5 + 10;
const int mod = 1e9 + 7;

int a[mx];
int b[mx][2];
bool v[mx];
set <int> st[2];

int main() {
	int t, n, k;
	scanf("%d", &t);
	while (t--) {
		st[0].clear();
		st[1].clear();
		scanf("%d", &n);
		for (int i=1;i<=n;i++) {
			st[0].insert(i);
			st[1].insert(i);
			scanf("%d", a+i);
		}
		bool flag = 1;
		for (int i=1;i<=n;i++) {
			int id;
			if (st[0].count(a[i])) {
				id = 0;
			} else if (st[1].count(a[i])) {
				id = 1;
			} else {
				flag = 0;
				break;
			}
			b[i][id] = a[i];
			v[i] = id ^ 1;
			st[id].erase(a[i]);
		}
		if (flag == 0) {
			puts("NO");
			continue;
		}
		for (int i=1; i<=n; i++) {
			int id = v[i];
			auto it = st[id].upper_bound(a[i]);
			if (it == st[id].begin()) {
				flag = 0;
				break;
			}
			it--;
			b[i][id] = *it;
			st[id].erase(it);
		}
		if (flag == 0)
			puts("NO");
		else {
			puts("YES");
			for (int i=0; i<2;i++) {
				for (int j=1;j<=n;j++) {
					printf("%d ", b[j][i]);
				}
				puts("");
			}
		}
	}

	return 0;
}

D - Lucky Permutation

题目链接:Problem - D - Codeforces

解题思路:以位置和对应值建立一条有向边,可以得到若干个圈。我们每次操作可以将某个圈减少一个点,让这个点自环。也可以合并两个圈,但是这个操作显然是没有意义的。最终要留下的是一对相邻的值形成的环即可。那么如果一个圈内有两个相邻的值,那么肯定可以进行操作之后只留下这两个相邻的值,如果没有,则需要再加一次操作让两个相邻自环的点变成一个圈即可。

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)|1
typedef long long ll;
const int mx = 2e5 + 10;
const int mod = 1e9 + 7;

set<int> st;
bool vis[mx];
int a[mx];

int main() {
	int t, n, k;
	scanf("%d", &t);
	while (t--) {
		scanf("%d", &n);
		for (int i=1;i<=n;i++) {
			scanf("%d", a+i);
			vis[i] = 0;
		}
		bool flag = 0;
		int ans = 0;
		for (int i=1;i<=n;i++) {
			if (!vis[i]) {
				st.clear();
				int j = i, last = -1;
				while (!vis[j]) {
					vis[j] = 1;
					st.insert(j);
					j = a[j];
				}
				for (auto it: st) {
					if (it == last + 1) {
						flag = 1;
						break;
					}
					last = it;
				}
				ans += st.size() - 1;
			}
		}
		//printf("%d %d\n", ans, flag);
		if (ans == 0)
			puts("1");
		else
			printf("%d\n", ans - (flag? 1:-1));
	}

	return 0;
}

E - Partial Sorting

题目链接:Problem - E - Codeforces

解题思路:可以得出最多操作数只需要3次,0次的情况只有当这个序列是有序的时候。那么我们就只需要讨论1、2、3的情况即可,我们将三段分别标记为1、2、3。

        1) 1的情况肯定是1段是有序的或者3段是有序的。那么已知一段有序的情况下其他的排列就是(2n)! - 1,减1是因为排除掉完全有序0的情况。因为1和3都可以,所以要*2。但是多算了一次1段和3段都有序的情况,所以要减去2段的全排列,n! - 1,减一原因同上。

        2)算2的情况和1其实类似,都有对称性,都需要容斥减一重复,以1段举例,2次的情况要保证1-n的数都在1和2段之内,并且第三段不是(2n+1~3n)有序的,不然就是第一种情况了。那么就有(C_{2n}^{n}n! - 1)*((2n)! - n!),减一是排除掉1-n在第一段完全有序的情况。后面减去n!就是排除掉第三段(2n+1~3n)有序的情况。因为对称性,第三段也是如此,所以要*2。但是多算了一次1-n的数都在1和2段之内和(2n+1~3n)也在2和3段之内的情况。所以要减去这一个情况。枚举1-n的数在2段中的数量即可,然后其他的可以使用排列组合。时间复杂度O(nlogn)。

        3)3n! 减去0、1、2的个数就是3的个数了。

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)|1
typedef long long ll;
const int mx = 3e6 + 10;

ll c[mx], d[mx];

ll qpow(ll x, ll y, int mod) {
	ll ans = 1;
	while (y) {
		if (y & 1) ans = ans * x % mod;
		y >>= 1;
		x = x * x % mod;
	}
	return ans;
}

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	d[0] = c[0] = 1;
	for (int i=1;i<=3*n;i++)
		c[i] = c[i-1] * i % m;
	ll ans = 0, sum = 1;
	// 只需要一次的情况 
	ans += (c[2 * n] - 1) * 2 % m;
	ans -= (c[n] - 1) % m;
	ans = (ans + m) % m;
	sum = (sum + ans) % m;
	// 2次 
	ll c_2n_n = c[2*n] * qpow(c[n], m - 2, m) % m * qpow(c[n], m - 2, m) % m;
	ll v = (c_2n_n * c[n] % m - 1) % m * (c[2*n] - c[n] + m) % m;
	ans += v * 2 * 2 % m;
	sum += v * 2 % m;
	for (int i=1;i<=n;i++)
		d[i] = qpow(c[i], m - 2, m);
	
	// 容斥剪去相同的部分,也就是两边操作顺序都可以的 
	for (int i=1;i<=n;i++) {
		ll c_n_i = c[n] * d[n-i] % m * d[i] % m;
		ll c_2ni_n = c[2*n-i] * d[n-i] % m * d[n] % m; // c[2n-i,n]
		
		//两边可以操作的情况 
		v = c_n_i * c_n_i % m * c[i] % m;
		v = v * c_n_i % m * c[n-i] % m; // c[n, n-i]
		v = v * (c_2ni_n * c[n] % m - 1) % m * c[n] % m;
		ans = (ans - v * 2 + m) % m;
		sum = (sum - v + m) % m; 
	}
	ll c_2ni_n = c[2*n] * qpow(c[n], m - 2, m) % m * qpow(c[n], m - 2, m) % m; 
	v = (c[n] - 1) * (c_2ni_n * c[n] % m - 1) % m * c[n] % m; //1-n全在第一个区间的情况 
	ans = (ans - v * 2 + m) % m;
	sum = (sum - v + m) % m; 
	// 三次
	ans += (c[3*n] - sum + m) * 3 % m;
	printf("%lld\n", (ans + m) % m);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值