Codeforces Round #672 Div. 2

线段树是假的,逆元是假的,我也是假的,啥都是假的————补题有感

A. Cubes Sorting

给一串数组,如果能在 n(n+1)/2-1 次交换相邻两个数使其变为升序输出YES,反之输出NO

只有完全降序的数组需要 n(n+1)/2 次交换,判断即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <string>
#include <vector>
#define lc(i) (2*i+1)
using namespace std;
typedef long long ll;
const ll mod = 1e6 + 1e6 + 5;
ll INF = 1e18;
int dataa[mod] = {};
int tempa[mod] = {};
ll ans = 1;
int min(int b, int a) { if (a > b)return b; return a; }
string k1,k2;
void solve() {
	ll m;
	int flag = 0;
	cin >> m;
	for (int i = 0; i < m; i++)scanf("%d", &dataa[i]);
	for (int i = 0; i < m - 1; i++) { if (dataa[i] <= dataa[i + 1])flag = 1; }
	if (!flag)cout << "NO
";
	else cout << "YES
";
}
int main(){
	ll n;
	cin >> n;
	while (n--)
	{
		solve();
	}
}

B. Rock and Lever

给定一串数组,找出里面有多少对数字x,y有

[x&y geqslant x igoplus y ]

我们可以知道,当且仅当x和y二进制最高位的1所在位数相同,等式成立。先排序,然后遍历即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <string>
#include <vector>
#define lc(i) (2*i+1)
using namespace std;
typedef long long ll;
const ll mod = 1e5 + 1e5 + 5;
ll INF = 1e18;
ll dataa[mod] = {};
ll cnts[1000] = {};
int min(int b, int a) { if (a > b)return b; return a; }
string k1,k2;
void solve() {
	ll m;
	ll ans = 0;
	cin >> m;
	for (int i = 0; i < m; i++)scanf("%d", &dataa[i]);
	memset(cnts, 0, sizeof(cnts));
	sort(dataa, dataa + m);
	int cnt = 0;
	ll max = 1;									//max储存当前位最大的数+1
	for (int i = 0; i < m; i++) {
		while (dataa[i] >=max)max *= 2,cnt++;		//cnts[cnt]储存的是最高位为cnt位的数的个数
		cnts[cnt]++;
	}
	for (int i = 0; i <= cnt; i++)ans += ((ll)(cnts[i]) * (ll)((cnts[i] - 1)) / (ll)(2)); //求组合
	cout << ans << "
";
}
int main(){
	ll n;
	cin >> n;
	while (n--)
	{
		solve();
	}
}

C1. Pokémon Army (easy version)

给定一串数组,取子串b1 ,b2。。。,令b1-b2+b3。。。最大,输出最大值

没什么好说的,DP

有以下转移方程,令F0(i)为i之前一共选了偶数数个的最大值,F1(i)为i之前一共选了奇数个的最大值,则:

[egin{cases} F0_i=max(F1_{i-1}+a_i,F0_{i-1})\ F1_i=max(F0_{i-1}-a_i,F1_{i-1}) end{cases} ]

简单版我们可以直接两个变量分别储存F0i和F1i之后实时更新即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <string>
#include <vector>
#include <utility>
#include<queue>
#define lc(i) (2*i+1)
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const ll mod = 1e5 + 1e5 + 1e5+10;
ll dataa[mod];
string k1,k2;
void solve() {
	ll a,b;
	ll ans = 0;
	cin >> a >> b;
	for (int i = 1; i <= a; i++)scanf("%lld", &dataa[i]);
	ll max1 = dataa[1], max2 = 0;
	for (int i = 2; i <= a; i++) {
		ll tmp = dataa[i];
		if (max2)tmp = max(tmp, max2 + tmp);
		max2 = max(max2, max1 - dataa[i]);
		max1 = max(max1, tmp);
	}
	cout <<max1 << '
';
}
int main(){
	ll n;
	cin >> n;
	while (n--)
	{
		solve();
	}
}

C2. Pokémon Army (hard version)

比c1多了个交换数组中的两个值,然后求新数组新解的过程

问了下大佬,大佬教了我个动态dp(,然而我线段树写的太假了,研究了一天

首先我们知道矩阵乘法

[egin{bmatrix} x&y end{bmatrix} imes egin{bmatrix} a & b \ c & d end{bmatrix} = egin{bmatrix} x imes a+y imes c&x imes b+y imes d end{bmatrix} ]

只要运算支持分配律, 我们就能定义广义矩阵乘法,且新矩阵支持结合律

即用max替代+,+替代×

所以定义广义矩阵乘法

[egin{bmatrix} x&y end{bmatrix} imes egin{bmatrix} a & b \ c & d end{bmatrix} = egin{bmatrix} max(x+a,y+c)&max(x+b,y+d) end{bmatrix}\ 单位矩阵为 egin{bmatrix} 0 & -INF \ -INF & 0 end{bmatrix} ]

由上面的dp递推式,可以得出

[egin{bmatrix} F0_{i-1}&F1_{i-1} end{bmatrix} imes egin{bmatrix} 0 & -a_i \ a_i &0 end{bmatrix} = egin{bmatrix} max(F0_{i-1},F1_{i-1}+a_i)&max(F1_{i-1},F0_{i-1}-a_i) end{bmatrix}= egin{bmatrix} F0_i & F1_i \ end{bmatrix} ]

因此

[egin{bmatrix} F0_n & F1_n end{bmatrix}= egin{bmatrix} F0_0 & F1_0 end{bmatrix} prod_{i=0}^{n-1}egin{bmatrix} 0 & -a_i \ a_i &0 end{bmatrix} ]

用线段树维护一下矩阵的乘积即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <string>
#include <vector>
#include <utility>
#include<queue>
#define lc(i) (2*i+1)
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const ll mod = 1e5 + 1e5 + 1e5 + 10;
ll dataa[mod];
ll a, b, max_n;
const int INF = 0x3f3f3f3f3f3f;
struct mtx
{
	ll m1 = 0, m2 = -1 * INF, m3 = -1 * INF, m4 = 0;
};
ll n;
vector<mtx> mtx1;
mtx mul(mtx a, mtx b) {
	mtx tmp;
	tmp.m1 = max(a.m1 + b.m1, a.m2 + b.m3);
	tmp.m2 = max(a.m1 + b.m2, a.m2 + b.m4);
	tmp.m3 = max(a.m3 + b.m1, a.m4 + b.m3);
	tmp.m4 = max(a.m3 + b.m2, a.m4 + b.m4);
	return tmp;
}
void pushup(int k) {
	mtx1[k] = mul(mtx1[k << 1], mtx1[k << 1 | 1]);
}
void build(int k, int l, int r) {
	int a = dataa[l];
	if (l == r)mtx1[k].m1 = 0, mtx1[k].m2 = -1 * a, mtx1[k].m3 = a, mtx1[k].m4 = 0;

	else {
		int mid = l + (r - l) / 2;
		build(k << 1, l, mid);
		build(k << 1 | 1, mid + 1, r);
		pushup(k);
	}
}
void updata(int p, int v, int l, int r, int k) {
	if (l == r) {
		mtx1[k].m1 = 0, mtx1[k].m2 = -1 * v, mtx1[k].m3 = v, mtx1[k].m4 = 0;
	}
	else {
		int m = l + (r - l) / 2;
		if (p <= m)updata(p, v, l, m, k << 1);
		else updata(p, v, m + 1, r, k << 1 | 1);
		pushup(k);
	}
}
int main() {
	ll m;
	cin >> m;
	while (m--)
	{
		cin >> a >> b;
		n = a;
		mtx1 = vector<mtx>(a << 2);
		for (int i = 1; i <=a; i++) {
			scanf("%lld", &dataa[i]);
		}
		build(1, 1, n);
		printf("%lld
", max(mtx1[1].m3, mtx1[1].m4));
		for (int i = 0; i < b; i++) {
			int l, r;
			cin >> l >> r;
			swap(dataa[l], dataa[r]);
			updata(l, dataa[l], 1, n, 1);
			updata(r, dataa[r], 1, n, 1);
			printf("%lld
", max(mtx1[1].m3, mtx1[1].m4));
		}
		

	}
}

D. Rescue Nibel!

给定一些灯的开关时间,问要使同一时间有k盏灯开着,有多少种操作方法

先按灯打开时间排序,用小根堆优先队列维护该灯之前所有灯的结束时间。把所有小于当前灯打开时间的数pop掉,剩下数的个数就是能覆盖到当前灯的个数。然后ans加上覆盖个数对k-1求组合数,最后再把当前灯的结束时间加入堆中即可。

开始写了才发现我不会算逆元( tcl

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <utility>
#include<map>
#include<queue>
#include<sstream>
#define lc(i) (2*i+1)
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const ll mod = 998244353 ;
ll n=0,k=0;
P dataa[300000+15];
int cnt[1000];
ll f[300000 + 15];
ll inv[300000 + 15];
map<string, int> mp;
string all;
priority_queue<ll, vector<ll>, greater<long long> >  que;
ll qpow(ll a, ll b) {
	ll res = 1;
	a %= mod;
	while (b)
	{
		if (b & 1)
			res = (res * a) % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return res;
}
void pre(ll len) {
	f[0] = 1;
	for (int i = 1; i <= len; i++)
		f[i] = i * f[i - 1] % mod;
	inv[len] = qpow(f[len], mod - 2);//费马小定理
	for (int i = len - 1; i >= 0; i--)inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll c(ll n, ll m) {
	if (n < m || n < 0)return 0;
	return f[n] * inv[m] % mod * inv[n - m]%mod;
}
int main(){
	cin >> n>>k;
	//while (n--)
	ll ans = 0;
	for(int i=0;i<n;i++)
	{
		ll x, y;
		scanf("%lld %lld", &x, &y);
		dataa[i] = P(x, y);
	}
	sort(dataa, dataa + n);
	pre(n + 1);
	for (int i = 0; i < n; i++) {
		while (!que.empty() && que.top() < dataa[i].first)que.pop ();
		ans = (ans + c(que.size(), k - 1) % mod) % mod;
		que.push(dataa[i].second);
	}
	cout << ans;
}
K-ON!!
原文地址:https://www.cnblogs.com/pophirasawa/p/13741959.html