线段树是假的,逆元是假的,我也是假的,啥都是假的————补题有感
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;
}