I.Inverse Pair

题目描述:

For a sequence t1…n, we define the weight of it is the number of pairs(i,j) satisfy i<j and ti>tj.

Now give you a permutation a1…n, you need to choose a sequence b1…n satisfies bi∈{0,1} to minimize the weight of sequence c1…n which satisfies ci=ai+bi.

输入描述:
The first line has one integer nn

The second line has n integers a1…n

It’s guaranteed that ai is a permutation of {1,2…n}

1≤n≤2×10^5

输出描述:
Output the minimum weight of c1…nc1…n you can get.

输入
5
4 3 2 5 1

输出
5

思路
题意是b的每个数字都能加上1或者不变,求排列的逆序对数。
原本求逆序数是要严格大于,即每个数前面有多少个大于这个数的数字,然后求和。
如果a的前面正好有a+1这个数,那么将a加上1就可以减少一个逆序数。所以先预处理一下这个数组,将其中的一部分数+1,后面的就是正常求逆序数的过程。
暴力肯定会超时,这里用树状数组的方法来求解逆序对数

代码:

//树状数组求解逆序对
#include <bits/stdc++.h>
#define N 500050
typedef long long ll;
using namespace std;
struct Tr {
	ll num;
	ll j;
};
ll n;
Tr tr[N];
ll inp[N];
ll lowbit(ll x) {
	return x & -x;
}
bool mycmp(Tr x, Tr y) {
	return x.j > y.j || (x.j == y.j && x.num > y.num);
}
ll fid(ll x) {
	ll t = 0;
	while (x) {
		t += inp[x];
		x -= lowbit(x);
	}
	return t;
}
void add(ll x) {
	while (x <= n) {
		inp[x]++;
		x += lowbit(x);
	}
}
int a[200005];
bool p[200005];
signed main() {
	cin.tie(0)->ios::sync_with_stdio(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		if (p[a[i] + 1]) {
			a[i]++;
		}
		p[a[i]] = 1;
	}
	for (int i = 1; i <= n; i++) {
		tr[i].num = i;
		tr[i].j = a[i];
	}
	ll ans = 0;
	sort(tr + 1, tr + n + 1, mycmp);
	for (int i = 1; i <= n; i++) {
		ll k = tr[i].num;
		ans += fid(k);
		add(k);
	}
	cout << ans;
	return 0;
}
对于仿射密码解密算法,我们需要先了解仿射密码的加密过程: - 明文中的每个字母都被映射到一个数字,比如 A 映射到 0,B 映射到 1,以此类推。 - 对于每个字母,我们先用一个乘法因子 a 将其乘上,再加上一个偏移量 b,最后对 26 取模。 - 加密后的数字再映射回字母即可得到密文。 因此,仿射密码的解密过程就是先将密文中的每个字母映射回数字,然后通过乘法逆元和减法得到加密时的数字,再映射回明文。 下面是一个简单的 C 语言实现: ```c #include <stdio.h> int mod_inverse(int a, int m) { // 通过扩展欧几里得算法求乘法逆元 int x0 = 1, x1 = 0, y0 = 0, y1 = 1; while (m != 0) { int q = a / m; int tmp = m; m = a % m; a = tmp; tmp = x1; x1 = x0 - q * x1; x0 = tmp; tmp = y1; y1 = y0 - q * y1; y0 = tmp; } return x0; } int main() { char ciphertext[] = "KHOOR ZRUOG"; int a = 5, b = 8, m = 26; // 加密时的乘法因子和偏移量 int a_inv = mod_inverse(a, m); for (int i = 0; ciphertext[i] != '\0'; i++) { if (ciphertext[i] == ' ') { printf(" "); } else { int c = ciphertext[i] - 'A'; int p = (a_inv * (c - b + m)) % m; printf("%c", 'A' + p); } } printf("\n"); return 0; } ``` 对于 Playfair 密码,其加密过程如下: - 将明文分成成对的字母,如果有成对的字母相同,插入一个填充字母,比如 X。 - 对于每一对字母,如果它们在同一行或同一列,将它们分别替换为同一行或同一列中的下一个字母(循环),否则将它们替换为它们所在行列的另外一个字母。 - 将替换后的字母拼接在一起即为密文。 因此,Playfair 密码的解密过程就是先将密文分成成对的字母,然后根据加密时的规则逆向替换回明文。 下面是一个简单的 C 语言实现: ```c #include <stdio.h> #include <string.h> int find_row(char c, char key[5][5]) { // 找到字母所在的行 for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { if (key[i][j] == c) { return i; } } } return -1; } int find_col(char c, char key[5][5]) { // 找到字母所在的列 for (int j = 0; j < 5; j++) { for (int i = 0; i < 5; i++) { if (key[i][j] == c) { return j; } } } return -1; } char decrypt_char(char c1, char c2, char key[5][5]) { if (c1 == c2) { // 填充字母,直接返回 return c1; } int row1 = find_row(c1, key), col1 = find_col(c1, key); int row2 = find_row(c2, key), col2 = find_col(c2, key); if (row1 == row2) { // 同一行,向左替换 col1 = (col1 - 1 + 5) % 5; col2 = (col2 - 1 + 5) % 5; return key[row1][col1] * 26 + key[row2][col2]; } else if (col1 == col2) { // 同一列,向上替换 row1 = (row1 - 1 + 5) % 5; row2 = (row2 - 1 + 5) % 5; return key[row1][col1] * 26 + key[row2][col2]; } else { // 不在同一行列,替换为对角线上的字母 return key[row1][col2] * 26 + key[row2][col1]; } } int main() { char ciphertext[] = "BG AJ ZA KA"; char key[5][5] = { {'B', 'G', 'A', 'F', 'H'}, {'C', 'D', 'E', 'K', 'L'}, {'M', 'N', 'O', 'P', 'Q'}, {'R', 'S', 'T', 'U', 'V'}, {'W', 'X', 'Y', 'Z', 'I'} }; for (int i = 0; ciphertext[i] != '\0'; i += 3) { char c1 = ciphertext[i], c2 = ciphertext[i+1]; char p1 = decrypt_char(c1, c2, key) / 26; char p2 = decrypt_char(c1, c2, key) % 26; printf("%c%c", p1 + 'A', p2 + 'A'); } printf("\n"); return 0; } ``` 注意,这里我们将两个字母替换为一个数字,以便于存储和处理。在实际使用中,我们可以使用一个结构体来表示一个字母对,比如: ```c struct LetterPair { char c1; char c2; }; // 或者使用一个整数来表示一个字母对,比如: // int pair = (c1 - 'A') * 26 + (c2 - 'A'); // char c1 = pair / 26 + 'A'; // char c2 = pair % 26 + 'A'; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值