文章目录
- 不定时更新
- C1D-多项式合并:双指针-哈希表
- C2E-套娃排序:树状数组
- C1H-逆序对数量:归并排序 OR 树状数组
- C1I-淘汰赛:分治法 OR 建树
- C1J-最大回文数:贪心
- C2C-随机数生成器:计数排序
- C2D-堆实现C语言
- C2F-多彩数列:滑动窗口
- C2H-寻找不共线的点:暴力
- C2I-矩阵连乘:快速幂
- C2J-三叉卡特兰数:递归
- E1C-模拟队列:模拟
- E1D-连分数计算:快速幂+费马小定理
- E1G-最多互质数:“待补充”
- E1I-掷三个骰子:概率论(可以前缀和优化)
- E1J-素点个数:
- C3A-钢管切割:动态规划
- C3C-流水线调度:动态规划
- C3E-矩阵连乘效率:区间动态规划
- C3F-导弹轰炸(小偷问题):动态规划
- C3H-OBST:区间动态规划
- C3I-查字典:最优二叉搜索树
- E2D-n元数:递归
- E2F-超级计算机:维护优先队列
- E2J-右移拿金币:
不定时更新
C1D-多项式合并:双指针-哈希表
题意:多项式合并:每组数据6行,n /n个系数 /n个次数 /m / m个系数/ m个次数
#include <stdio.h>
//#define abyss
#define MAXN 100005
long long a[MAXN], b[MAXN], A[MAXN], B[MAXN];
long long c[2 * MAXN], C[2 * MAXN];
int main(){
int tt; scanf("%d", &tt);
while(tt--) {
int n, m; scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for(int i = 1; i <= n; i++) scanf("%lld", &A[i]);
scanf("%d", &m);
for(int i = 1; i <= m; i++) scanf("%lld", &b[i]);
for(int i = 1; i <= m; i++) scanf("%lld", &B[i]);
int topA = 1, topB = 1, cnt = 0;
while(topA <= n && topB <= m) {
if(A[topA] == B[topB]) {
long long tmp = a[topA] + b[topB];
if(tmp) {c[++cnt] = tmp; C[cnt] = A[topA];}
topA++; topB++;
}
else if(A[topA] < B[topB])
c[++cnt] = a[topA], C[cnt] = A[topA++];
else
c[++cnt] = b[topB], C[cnt] = B[topB++];
}
while(topA <= n) {c[++cnt] = a[topA]; C[cnt] = A[topA++];}
while(topB <= m) {c[++cnt] = b[topB]; C[cnt] = B[topB++];}
printf("%d\n", cnt);
for(int i = 1; i <= cnt; i++) printf("%lld%c", c[i], " \n"[i == cnt]);
for(int i = 1; i <= cnt; i++) printf("%lld%c", C[i], " \n"[i == cnt]);
}
return 0;
}
C2E-套娃排序:树状数组
题意:每组数据2行,n/n个数字,输出第i个数字在前i个数字里面是第几小
可以直接暴力,树状数组解法
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
int j = i;
int cnt = 1;
while (j - 1 >= 0 && a[j] > a[j - 1]) {
swap(a[j - 1], a[j]);
j--;
cnt++;
}
cout << cnt << ' ';
}
cout << '\n';
}
return 0;
}
C1H-逆序对数量:归并排序 OR 树状数组
题意:每组数据两行,n/n个乱序整数,每次可以交换相邻数字,求变成升序交换次数(逆序对数)。题解
//树状数组
#include <bits/stdc++.h>
using namespace std;
class fenwick {
public:
int n;
vector<int> arr;
fenwick(int _n) : n(_n) { arr.resize(n); }
void modify(int x, int val) {
while (x < n) {
arr[x] += val;
x |= (x + 1);
}
}
int get(int x) {
int ret = 0;
while (x >= 0) {
ret += arr[x];
x = (x & (x + 1)) - 1;
}
return ret;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt; cin >> tt;
while (tt--) {
int n;
cin >> n;
fenwick tr(n + 1);
long long ans = 0;
for (int i = 0; i < n; i++) {
int a;
cin >> a;
ans += i - tr.get(a);
tr.modify(a, 1);
}
cout << ans << '\n';
}
return 0;
}
//归并排序
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n;
int a[N], b[N];
long long merge(int l, int mid, int r) {
for (int i = l; i <= r; i++) {
b[i] = a[i];
}
int i = l, j = mid + 1, k = l;
long long cnt = 0;
while (i <= mid && j <= r) {
if (b[i] <= b[j]) {
a[k++] = b[i++];
} else {
// b[j] < b[i] => b[j] < b[i], b[i + 1], ... b[mid]
// so the number is (mid - i + 1)
a[k++] = b[j++];
cnt += mid - i + 1;
}
}
while (i <= mid)
a[k++] = b[i++];
while (j <= r)
a[k++] = b[j++];
return cnt;
}
long long mergeSort(int l, int r) {
if (l >= r)
return 0;
long long cnt = 0;
int mid = (l + r) / 2;
cnt += mergeSort(l, mid);
cnt += mergeSort(mid + 1, r);
cnt += merge(l, mid, r);
return cnt;
}
int main() {
// ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
long long ans = mergeSort(0, n - 1);
cout << ans << '\n';
}
return 0;
}
C1I-淘汰赛:分治法 OR 建树
题意:每组2行数:n / 2 n 2^n 2n个数,两个一组比较,小者淘汰。输出每个数遇到的最大的数。题解
//分治法
#include <bits/stdc++.h>
using namespace std;
int t, n;
int v[1 << 18], ans[1 << 18];
int comp(int l, int r) {
if (r <= l + 1)
return l;
int p1 = comp(l, (l + r) >> 1);
int p2 = comp((l + r) >> 1, r);
ans[p1] = max(ans[p1], v[p2]);
ans[p2] = max(ans[p2], v[p1]);
if (v[p2] > v[p1])
return p2;
return p1;
}
int main() {
cin >> t;
while (t--) {
cin >> n;
for (int i = 0; i < (1 << n); i++) {
cin >> v[i];
ans[i] = -1;
}
comp(0, 1 << n);
for (int i = 0; i < (1 << n); i++)
cout << ans[i] << " ";
cout << endl;
}
return 0;
}
//建树,先放个图
C1J-最大回文数:贪心
题意:每组一个数:n( 1 ≤ n ≤ 1 0 500 1\le n\le 10^{500} 1≤n≤10500),输出不超过n的最大回文数。题解
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 505;
char a[N], b[N];
int n, mid;
int leq() {
for (int i = 1; i <= n; i++) {
if (b[i] < a[i])
return 1;
if (b[i] > a[i])
return 0;
}
return 1;
}
void solve() {
scanf("%s", a + 1);
n = strlen(a + 1);
mid = 1;
for (int i = 1, j = n; i <= j; i++, j--) {
b[i] = b[j] = a[i];
mid = i;
}
b[n + 1] = 0;
if (leq()) {
printf("%s\n", b + 1);
return;
}
b[mid]--;
for (int i = mid; i && b[i] < '0'; i--) {
b[i] += 10;
b[i - 1]--;
}
for (int i = 1; i <= mid; i++)
b[n - i + 1] = b[i];
if (b[1] != '0' && leq()) {
printf("%s\n", b + 1);
return;
}
for (int i = 1; i < n; i++)
b[i] = '9';
b[n] = 0;
printf("%s\n", b + 1);
}
int main() {
int T;
scanf("%d", &T);
while (T--)
solve();
return 0;
}
C2C-随机数生成器:计数排序
题意:每组两行数 :n/n个数 对于一个数组 A = [ a 1 , a 2 , ⋯ , a n ] A = [a_1, a_2, \cdots, a_n] A=[a1,a2,⋯,an] A = [ a 1 , a 2 , ⋯ , a n ] A = [a_1, a_2, \cdots, a_n] A=[a1,a2,⋯,an],定义
f ( A ) = ∑ i = 1 n o r d e r ( a i ) ⋅ a i f(A) = \sum_{i = 1}^n \mathrm{order}(a_i) \cdot a_i f(A)=i=1∑norder(ai)⋅ai
其中
o r d e r ( a i ) = 1 + ∑ a j < a i 1 ≤ j ≤ n 1 \mathrm{order}(a_i) = 1 + \sum_{\substack{a_j < a_i \\ 1 \leq j \leq n}} 1 order(ai)=1+aj<ai1≤j≤n∑1
例:一个数 a i a_i ai a i a_i ai 的 o r d e r \mathrm{order} order 定义为 数组内全部元素 比它小的数的个数加一,例如对于 A = [ 1 , 1 , 2 , 3 , 3 , 3 , 4 ] A = [1, 1, 2, 3, 3, 3, 4] A=[1,1,2,3,3,3,4] 有:
o r d e r ( 1 ) = 1 , o r d e r ( 2 ) = 3 , o r d e r ( 3 ) = 4 , o r d e r ( 4 ) = 7 \mathrm{order}(1) = 1, \ \mathrm{order}(2) = 3, \ \mathrm{order}(3) = 4, \ \mathrm{order}(4) = 7 order(1)=1, order(2)=3, order(3)=4, order(4)=7
因此
f ( A ) = 1 ⋅ 1 + 1 ⋅ 1 + 3 ⋅ 2 + 4 ⋅ 3 + 4 ⋅ 3 + 4 ⋅ 3 + 7 ⋅ 4 = 72 f(A) = 1 \cdot 1 + 1 \cdot 1 + 3 \cdot 2 + 4 \cdot 3 + 4 \cdot 3 + 4 \cdot 3 + 7 \cdot 4 = 72 f(A)=1⋅1+1⋅1+3⋅2+4⋅3+4⋅3+4⋅3+7⋅4=72
#include <bits/stdc++.h>
using namespace std;
const int N = 100003;
int nextRand() {
static unsigned int rnd_num = 0x80000001;
static int mod = 1e5 + 3;
rnd_num ^= rnd_num >> 10;
rnd_num ^= rnd_num << 9;
rnd_num ^= rnd_num >> 25;
assert(rnd_num != 0);
return rnd_num % mod;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector<int> a(N);
for (int i = 0; i < n; i++) {
a[nextRand()]++;
}
long long ans = 0;
int ord = 1;
for (int i = 0; i < N; i++) {
ans += (long long) i * ord * a[i];
ord += a[i];
}
cout << ans << '\n';
}
return 0;
}
C2D-堆实现C语言
题意:
第一行一个正整数 n n n( 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1≤n≤105),表示操作的数量。
接下来 n n n 行,每行为一个操作,格式如下:
1 1 1 x x x:向堆中插入元素 x x x x x x( 1 ≤ x ≤ 1 0 9 1 \le x \le 10^9 1≤x≤109 1 ≤ x ≤ 1 0 9 1 \le x \le 10^9 1≤x≤109)。
2 2 2:删除堆顶元素。
3 3 3:查询堆顶元素。
数据保证后两种操作时堆非空。
输出
对于每次查询堆顶元素时,输出一行一个正整数,表示此时堆顶元素的值。
在所有操作结束后,将堆中的元素从大到小依次输出到一行中。
#include<stdio.h>
#define maxn 1000005
#define inf 0x3f3f3f3f
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
int n, sz, a[maxn];
void swap(int *a, int *b) {
int t = *a;
*a = *b, *b = t;
}
void maxHeapify(int x) {
int l = ls(x), r = rs(x), larg;
if (l <= sz && a[l] > a[x]) larg = l;
else larg = x;
if (r <= sz && a[r] > a[larg]) larg = r;
if (larg != x) swap(&a[x], &a[larg]), maxHeapify(larg);
}
void increaseKey(int i, int key) {
a[i] = key;
while (i > 1 && a[i / 2] < a[i]) {
swap(&a[i / 2], &a[i]);
i = i / 2;
}
}
void insert(int x) {
sz += 1;
a[sz] = -inf;
increaseKey(sz, x);
}
int top() {
return a[1];
}
void pop() {
if (sz < 1) exit(1);
a[1] = a[sz];
sz -= 1;
maxHeapify(1);
}
int main() {
scanf("%d", &n);
int i;
for (i = 1; i <= n; i++) {
int op, x;
scanf("%d", &op);
if (op == 1) {
scanf("%d", &x);
insert(x);
}
if (op == 2) pop();
if (op == 3) printf("%d\n", top());
}
while (sz) {
printf("%d ", top()), pop();
}
}
C2F-多彩数列:滑动窗口
题意:每组数据两行 n k/ n个数,若能把正整数序列变成多彩的,那么输出操作次数,否则输出-1
多彩:如果 A A A 中存在连续的 k k k 个数恰好包含 1 , 2 , … , k 1, 2, \dots, k 1,2,…,k,则称 A A A 是多彩的。
你可以执行任意次以下操作:
- 选择不相同的两个下标 i , j i, j i,j( 1 ≤ i < j ≤ n 1\leq i < j\leq n 1≤i<j≤n),交换 A i A_i Ai 与 A j A_j Aj
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int n, k, ans, cnt;
int a[N], tcnt[N], vcnt[N];
void solve() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= k; i++)
tcnt[i] = vcnt[i] = 0;
cnt = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (!tcnt[a[i]])
cnt++;
tcnt[a[i]]++;
}
if (cnt != k) {
printf("-1\n");
return;
}
ans = cnt = 0;
for (int i = 1; i <= n; i++) {
if (!vcnt[a[i]])
cnt++;
vcnt[a[i]]++;
if (i > k) {
vcnt[a[i - k]]--;
if (!vcnt[a[i - k]])
cnt--;
}
if (cnt > ans)
ans = cnt;
}
ans = k - ans;
printf("%d\n", ans);
}
int main() {
int T;
scanf("%d", &T);
while (T--)
solve();
return 0;
}
C2H-寻找不共线的点:暴力
题意: 每组数据第一行n<1000,接下来n行,每行x,y,z表示三维坐标,寻找这样一个点A,使得A与所给点集任意两个不共线。存在输出坐标,否则输出-1.题解
暴力破解,枚举m个点,判断所有直线, O ( m n 2 ) O(mn^2) O(mn2)
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 1005;
int n;
int coord[N][3];
int out[3];
int gcd(int a, int b) {
if (a == 0 || b == 0)
return a + b;
if (a % b == 0)
return b;
return gcd(b, a % b);
}
int check() {
for (int d = 0; d < 3; d++)
if (out[d] < 1 || out[d] > n)
return -1;
for (int i = 1; i <= n; i++) {
int cnt = 0;
for (int d = 0; d < 3; d++)
if (out[d] == coord[i][d])
cnt++;
if (cnt == 3)
return -1;
}
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++) {
int px = coord[j][0] - coord[i][0];
int py = coord[j][1] - coord[i][1];
int pz = coord[j][2] - coord[i][2];
int qx = out[0] - coord[i][0];
int qy = out[1] - coord[i][1];
int qz = out[2] - coord[i][2];
int gp = gcd(gcd(abs(px), abs(py)), abs(pz));
int gq = gcd(gcd(abs(qx), abs(qy)), abs(qz));
px /= gp, py /= gp, pz /= gp;
qx /= gq, qy /= gq, qz /= gq;
if (px == qx && py == qy && pz == qz)
return -1;
if (px == -qx && py == -qy && pz == -qz)
return -1;
}
return 0;
}
int rand(int l, int r) {
return rand() % (r - l + 1) + l;
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
for (int d = 0; d < 3; d++)
scanf("%d", &coord[i][d]);
for (int i = 1; i <= 100; i++) {//枚举点并检查
for (int d = 0; d < 3; d++)
out[d] = rand(1, n);
if (check())
continue;
printf("%d %d %d\n", out[0], out[1], out[2]);
return;
}
printf("-1\n");
}
int main() {
int T;
scanf("%d", &T);
while (T--)
solve();
return 0;
}
C2I-矩阵连乘:快速幂
题意:每组数据 第一行-方阵阶数n 接下来n行,表示整数方阵A。输出 A n A^n An
#include <bits/stdc++.h>
#define maxn 205
typedef long long ll;
const int p = 1e9 + 7;
using namespace std;
int n;
ll k;
struct Martix {
ll a[maxn][maxn];
};
inline Martix multiply(Martix x, Martix y) {
Martix z;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
z.a[i][j] = 0;
for (int k = 1; k <= n; k++) {
z.a[i][j] += x.a[i][k] * y.a[k][j];
z.a[i][j] %= p;
}
}
}
return z;
}
inline Martix fpow(Martix x, ll k) {
Martix y;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) y.a[i][j] = 1;
else y.a[i][j] = 0;
}
}
while (k) {
if (k & 1) y = multiply(y, x);
x = multiply(x, x);
k >>= 1;
}
return y;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%", &n);
k = n;
Martix x;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%lld", &x.a[i][j]);
}
}
x = fpow(x, k);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
printf("%lld ", x.a[i][j]);
}
printf("\n");
}
}
}
C2J-三叉卡特兰数:递归
C n = ∑ i + j + k = n − 1 i , j , k ≥ 0 C i C j C k C_n = \sum_{\substack{i + j + k = n - 1 \\ i, j, k \geq 0}} C_iC_jC_k Cn=i+j+k=n−1i,j,k≥0∑CiCjCk
题意: C 0 = 1 C_0=1 C0=1,每组数据一个n,求解第n项
#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 1;
const int mod = 998244353;
long long f[N], s[N];
void init() {
f[0] = 1;
s[0] = 1;
for (int n = 1; n < N; n++) {
for (int i = 0; i <= n - 1; i++) {
f[n] += f[i] * s[n - i - 1];
f[n] %= mod;
}
for (int i = 0; i <= n; i++) {
s[n] += f[i] * f[n - i];
s[n] %= mod;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
init();
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
cout << f[n] << '\n';
}
return 0;
}
E1C-模拟队列:模拟
题意:第一行n,t表示n个同学,t次操作,初始时每个同学单独一队
接下来 t t t 行,每行为一个操作,格式如下:
1 x y 1\ x\ y 1 x y: 以学生 y y y 为队首的队伍顺次排在以学生 x x x 为队首的队伍末尾,保证学生 x , y x, y x,y均为所在队伍的队首。
2 x 2\ x 2 x:查询学生 x x x 前方同学的编号,如果学生 x x x 前方没有同学,则输出 0 0 0。
3 x 3\ x 3 x:查询学生 x x x 后方同学的编号,如果学生 x x x 后方没有同学,则输出 0 0 0。
#include<bits/stdc++.h>
#define maxn 100005
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch > '9' || ch < '0'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch -'0';ch = getchar();}
return x * f;
}
int n, T;
int tail[maxn], pre[maxn], nxt[maxn];
void solve(){
n = read(), T = read();
for(int i = 1; i <= n; i++) tail[i] = i, pre[i] = nxt[i] = 0;
while(T--){
int op;
scanf("%d", &op);
if(op == 1){
int x, y;
scanf("%d%d", &x, &y);
pre[y] = tail[x], nxt[tail[x]] = y;
tail[x] = tail[y];
}else if(op == 2){
int x;
scanf("%d", &x);
printf("%d\n", pre[x]);
}else{
int x;
scanf("%d", &x);
printf("%d\n", nxt[x]);
}
}
}
int main(){
int t = read();
while(t--) {
solve();
}
return 0;
}
E1D-连分数计算:快速幂+费马小定理
题意:
可用一组序列 表示,我们记其第 n n n 项表示为
p n q n = a 0 + 1 a 1 + 1 a 2 + 1 a 3 + 1 ⋱ + 1 a n \frac{p_n}{q_n} = a_0 + \frac{1}{a_1 + \frac{1}{a_2 + \frac{1}{a_3 + \frac{1}{\ddots + \frac{1}{a_n}}}}} qnpn=a0+a1+a2+a3+⋱+an11111
现给出 a 0 , a 1 , ⋯ , a n a_0, a_1, \cdots, a_n a0,a1,⋯,an,求出其表示的分数 p n q n \frac{p_n}{q_n} qnpn 在模 998244353 998244353 998244353 意义下的值。即输出 r n = p n ⋅ q n − 1 m o d 998244353 r_n = p_n \cdot q_n^{-1} \bmod 998244353 rn=pn⋅qn−1mod998244353,其中 q n − 1 满足 q n ⋅ q n − 1 ≡ 1 ( m o d 998244353 ) q_n^{-1} 满足q_n \cdot q_n^{-1} \equiv 1 \pmod{998244353} qn−1满足qn⋅qn−1≡1(mod998244353)。测试数据保证 q n q_n qn 的逆元 q n − 1 q_n^{-1} qn−1 存在
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
long long fp(long long a, long long x) {
long long ret = 1;
while (x) {
if (x & 1) {
ret *= a;
ret %= mod;
}
a *= a;
a %= mod;
x >>= 1;
}
return ret;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector<long long> a(n + 1);
vector<long long> p(n + 1), q(n + 1);
for (int i = 0; i <= n; i++)
cin >> a[i];
p[0] = a[0];
q[0] = 1;
p[1] = (a[0] * a[1] + 1) % mod;
q[1] = a[1];
for (int i = 2; i <= n; i++) {
p[i] = (a[i] * p[i - 1] + p[i - 2]) % mod;
q[i] = (a[i] * q[i - 1] + q[i - 2]) % mod;
}
cout << p[n] * fp(q[n], mod - 2) % mod << '\n';
}
return 0;
}
E1G-最多互质数:“待补充”
题意:每组数据两行,第一行n,第二行n个数,在最大公约数不为 1 的前提下,从给定的 n个整数中选择尽可能多的整数,输出选择的整数个数
#include <cstdio>
using namespace std;
const int P = 100;
const int N = 61670;
int p[P], pcnt,n, cnt;
int a[N];
void pre() {
for (int i = 2; i <= P; i++) {
int dcnt = 0;
for (int j = 2; j < i; j++)
if (i % j == 0)
dcnt++;
if (!dcnt)
p[++pcnt] = i;
}
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
cnt = 0;
for (int i = 1; i <= pcnt; i++) {
int pv = p[i];
int cur = 0;
for (int j = 1; j <= n; j++)
if (a[j] % pv == 0)
cur++;
if (cur > cnt)
cnt = cur;
}
printf("%d\n", cnt);
}
int main() {
pre();
int T;
scanf("%d", &T);
while (T--)
solve();
return 0;
}
E1I-掷三个骰子:概率论(可以前缀和优化)
题意:每组数据一行三个数,a,b,c,表示有三个骰子,求随机掷骰子得到三者之和概率最大值
#include <bits/stdc++.h>
using namespace std;
#define MAXN 800
int nums[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int tt;
cin >> tt;
while (tt--) {
int a, b, c;
cin >> a >> b >> c;
for (int i = 1; i <= a; i++)
for (int j = 1; j <= b; j++)
for (int k = 1; k <= c; k++)
nums[i + j + k]++;
int MAX = 0, res = 0;
for (int i = 3; i <= a + b + c; i++)
if (MAX < nums[i])
MAX = nums[i], res = i;
cout << res << "\n";
memset(nums, 0, sizeof(nums));
}
return 0;
}
E1J-素点个数:
题意:每组数据一行,每行 4 4 4 个正整数 x 1 , y 1 , x 2 , y 2 ( 1 ≤ x 1 ≤ x 2 ≤ 1 0 6 , 1 ≤ y 1 ≤ y 2 ≤ 1 0 6 ) x_1, y_1, x_2, y_2 \ (1 \leq x_1 \leq x_2 \leq 10^6, \ 1 \leq y_1 \leq y_2 \leq 10^6) x1,y1,x2,y2 (1≤x1≤x2≤106, 1≤y1≤y2≤106) 表示矩形左下角和右上角的点坐标,输出:矩形范围内满足 x + y , x − y x + y, x - y x+y,x−y都为素数的点的个数题解
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 5;
bool vis[N];
int pre[N];
vector<int> primes;
void init() {
for (int i = 2; i < N; i++) {
if (!vis[i]) primes.push_back(i);
for (auto p: primes) {
if (1LL * i * p >= N) break;
vis[i * p] = true;
if (i % p == 0) break;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
init();
int pos = 1; // skip 2
for (int i = 1; i < N; i++) {
pre[i] = pre[i - 1];
if (i == primes[pos]) {
pre[i]++;
pos++;
}
}
int tt;
cin >> tt;
while (tt--) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
long long ans = 0;
int at = 1;
while (primes[at] < x1 + y1) {
at++;
}
for (; primes[at] <= x2 + y2; at++) {
int p1 = primes[at];
int l = max(2 * x1 - p1, p1 - 2 * y2);
int r = min(2 * x2 - p1, p1 - 2 * y1);
if (l <= r && 0 <= r) {
ans += pre[r] - (l - 1 >= 0 ? pre[l - 1] : 0);
}
}
cout << ans << '\n';
}
return 0;
}
C3A-钢管切割:动态规划
题意:第一行一个正整数 n n n( 1 ≤ n ≤ 1 0 4 1 \le n \le 10^4 1≤n≤104),表示钢管的总长度。第二行 n n n 个正整数 p 1 , p 2 , … , p n p_1,p_2,\ldots,p_n p1,p2,…,pn( 1 ≤ p i ≤ 1 0 7 1 \le p_i \le 10^7 1≤pi≤107),表示长度 i i i钢管的价格。
输出:第一行一个正整数,表示最大总销售价格。第二行一个正整数 k k k,表示钢管被分割成 k k k 段。第三行 k k k 个正整数 a 1 , … , a k a_1, \dots, a_k a1,…,ak,表示钢管的分割方式,需保证 ∑ a i = n \sum a_i = n ∑ai=n
#include<bits/stdc++.h>
#define maxn 200005
typedef long long ll;
using namespace std;
ll read(){
ll x = 0, f = 1;char ch = getchar();
while(ch > '9' || ch < '0'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
int n, p[maxn];
ll f[maxn];
vector<int> ans;
void solve(){
n = read();
for(int i = 1; i <= n; i++) p[i] = read();
for(int i = 1; i <= n; i++){
for(int j = 0; j < i; j++){
f[i] = max(f[i], f[j] + p[i - j]);
}
}
printf("%lld\n", f[n]);
int now = n;
while(now){
for(int i = 1; i <= now; i++){
if(f[now] == f[now - i] + p[i]){
ans.push_back(i);
now -= i;
break;
}
}
}
printf("%d\n", ans.size());
for(int i = 0; i < ans.size(); i++) printf("%d%c", ans[i], i == ans.size() - 1 ? '\n' : ' ');
}
int main() {
int t;
t = 1;
while(t--)
solve();
return 0;
}
C3C-流水线调度:动态规划
题意:
#include <bits/stdc++.h>
using namespace std;
const long long inf = 1e18;
const int M = 1e5 + 5;
int p[3][M], t[3][3];
long long dp[3][M];
void solve() {
int m;
cin >> m;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < m; j++) {
cin >> p[i][j];
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cin >> t[i][j];
}
}
for (int j = 0; j < m; j++) {
for (int i = 0; i < 3; i++) {
dp[i][j] = inf;
for (int k = 0; k < 3; k++) {
dp[i][j] = min(
dp[i][j],
(j - 1 >= 0 ? dp[k][j - 1] + t[k][i] : 0LL) + p[i][j]
);
}
}
}
long long ans = inf;
for (int i = 0; i < 3; i++) {
ans = min(ans, dp[i][m - 1]);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) solve();
return 0;
}
C3E-矩阵连乘效率:区间动态规划
题意:第一行一个整数 n n n( 2 ≤ n ≤ 300 2 \le n \le 300 2≤n≤300) ,表示矩阵的个数。第二行 n + 1 n+1 n+1 个正整数 a 1 , a 2 , … , a n + 1 a_1,a_2,\ldots,a_{n+1} a1,a2,…,an+1( 1 ≤ a i ≤ 1 0 3 1 \le a_i \le 10^3 1≤ai≤103),表示矩阵 A i A_i Ai 的行数和列数为 a i a_i ai 和 a i + 1 a_{i+1} ai+1。
输出:一行一个浮点数,表示运算次数最多是最少的多少倍,结果保留4位小数。
#include<bits/stdc++.h>
#define maxn 305
typedef long long ll;
using namespace std;
ll read(){
ll x = 0, f = 1;char ch = getchar();
while(ch > '9' || ch < '0'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
int n, a[maxn];
ll f[maxn][maxn];
void solve(){
n = read();
memset(f, 0x3f, sizeof(f));
for(int i = 1; i <= n + 1; i++) a[i] = read(), f[i][i] = 0;
for(int len = 2; len <= n; len++){
for(int l = 1; l + len - 1 <= n; l++){
int r = l + len - 1;
for(int k = l; k < r; k++){
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + 1ll * a[l] * a[k + 1] * a[r + 1]);
}
}
}
ll mx = f[1][n];
memset(f, 0, sizeof(f));
for(int len = 2; len <= n; len++){
for(int l = 1; l + len - 1 <= n; l++){
int r = l + len - 1;
for(int k = l; k < r; k++){
f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r] + 1ll * a[l] * a[k + 1] * a[r + 1]);
}
}
}
printf("%.4f\n", 1.0 * f[1][n] / mx);
}
int main() {
int t;
t = 1;
while(t--) solve();
return 0;
}
C3F-导弹轰炸(小偷问题):动态规划
题意:每组测试数据包含两行。第一行包含一个正整数 n ( 2 ≤ n ≤ 1 0 5 ) n(2 \le n \le 10^5) n(2≤n≤105)。第二行包含 n n n 个正整数 w 1 , w 2 , … , w n ( 1 ≤ w i ≤ 1 0 5 ) w_1, w_2, \dots, w_n (1 \le w_i \le 10^5) w1,w2,…,wn(1≤wi≤105),表示每个前哨站的重要程度。数据保证 ∑ n ≤ 4 ⋅ 1 0 5 \sum n \le 4 \cdot10^5 ∑n≤4⋅105 。不能轰炸连续两个前哨战。
输出:对于每组测试数据,输出一行一个整数,表示在导弹互不干扰的情况下,能够轰炸的前哨站的重要程度之和的最大值。
#include<bits/stdc++.h>
#define maxn 200005
typedef long long ll;
const double eps = 1e-9;
const int mod = 998244353;
using namespace std;
ll read(){
ll x = 0, f = 1;char ch = getchar();
while(ch > '9' || ch < '0'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
int n;
ll w[maxn], f[maxn][2];
void solve(){
n = read();
for(int i = 1; i <= n; i++) w[i] = read(), f[i][0] = f[i][1] = 0;
for(int i = 1; i <= n; i++){
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
f[i][1] = w[i] + (i == 1 ? 0 : max(f[i - 2][0], f[i - 2][1]));
}
printf("%lld\n", max(f[n][0], f[n][1]));
}
int main() {
int t;
t = read();
while(t--) solve();
return 0;
}
C3H-OBST:区间动态规划
题意:设权值为 i i i 的点深度为 d i d_i di,给定序列 p 1 , p 2 , … , p n p_1,p_2,\ldots,p_n p1,p2,…,pn,定义该二叉搜索树的代价和为: ∑ i = 1 n ( d i + 1 ) p i \sum_{i=1}^n(d_i+1)p_i ∑i=1n(di+1)pi。求解集合 { p 1 , p 2 , … , p n } \{p_1,p_2,\ldots,p_n\} {p1,p2,…,pn} 所有二叉搜索树中最小的代价和,并输出任意一种最小代价的二叉搜索树结构。
输入:第一行 n,第二行n个正整数 p 1 , p 2 , … , p n p_1,p_2,\ldots,p_n p1,p2,…,pn( 1 ≤ p i ≤ 1 0 4 1 \le p_i \le 10^4 1≤pi≤104
输出:第一行一个正整数,表示最小的代价和。接下共 n n n 行,每行两个整数,分别表示点 i i i 的左儿子节点与右儿子节点,如果点 i i i 无左/右儿子节点,则对应位置输出 − 1 -1 −1。
#include<bits/stdc++.h>
#define maxn 505
typedef long long ll;
using namespace std;
ll read(){
ll x = 0, f = 1;char ch = getchar();
while(ch > '9' || ch < '0'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
int n, m, p[maxn], root;
ll dp[maxn][maxn], sum[maxn];
int rt[maxn][maxn];
int ch[maxn][2];
int dfs(int l, int r){
if(l > r) return -1;
int mid = rt[l][r];
ch[mid][0] = dfs(l, mid - 1);
ch[mid][1] = dfs(mid + 1, r);
return mid;
}
void solve(){
n = read();
for(int i = 1; i <= n; i++) p[i] = read(), sum[i] = sum[i - 1] + p[i];
for(int i = 1; i <= n; i++) dp[i][i] = p[i], ch[i][0] = ch[i][1] = -1, rt[i][i] = i;
for(int len = 2; len <= n; len++){
for(int l = 1; l + len - 1 <= n; l++){
int r = l + len - 1;
dp[l][r] = 1e18;
for(int k = l; k <= r; k++){
dp[l][r] = min(dp[l][r], dp[l][k - 1] + dp[k + 1][r] + sum[r] - sum[l - 1]);
}
for(int k = l; k <= r; k++){
if(dp[l][r] == dp[l][k - 1] + dp[k + 1][r] + sum[r] - sum[l - 1]) rt[l][r] = k;
}
}
}
printf("%lld\n", dp[1][n]);
dfs(1, n);
for(int i = 1; i <= n; i++){
printf("%d %d\n", ch[i][0], ch[i][1]);
}
}
int main() {
solve();
return 0;
}
C3I-查字典:最优二叉搜索树
题意:妮妮会按照以下规则在字典中查找单词:
当妮妮确定自己想要查找的单词在字典的第 l l l 个到第 r r r 个单词之间时,妮妮会查找第 f l , r ( l ≤ f l , r ≤ r ) f_{l, r} \ (l \leq f_{l, r} \leq r) fl,r (l≤fl,r≤r) 个单词,若第 f l , r f_{l, r} fl,r 个单词 s s s 已经是想要查找的单词 t t t,则会停止查字典。若 s s s 的字典序小于 t t t,则妮妮可以确定想要查找的单词 t t t 一定在第 f l , r + 1 f_{l, r} + 1 fl,r+1 到第 个单词之间。同理若 s s s 的字典序大于 t t t,则妮妮可以确定想要查找的单词 t t t 一定在第 l l l 到第 f l , r − 1 f_{l, r} - 1 fl,r−1 个单词之间。如此继续查找字典直到查到单词为止。
若字典中共有 m m m 个单词,显然妮妮每次查单词时最开始只能知道想要查找的单词在第 1 1 1 个到第 m m m 个之间,之后妮妮会按照上述规则进行查找,请你确定查找的策略 f l , r f_{l, r} fl,r 使得妮妮在读完这本杂志时,总的查找次数最小。请你求出最小的查找次数
第一行一个正整数 n n n( 1 ≤ n ≤ 1 0 6 1 \le n \le 10^6 1≤n≤106)表示单词的个数。第二行 n n n 个单词,保证每个单词长度均为 2 2 2,且均由小写字母组成。保证不同的单词个数不超过 300 300 300 个。
输出一行一个正整数,表示最少的查字典的次数。
#include<bits/stdc++.h>
#define maxn 505
typedef long long ll;
const double eps = 1e-9;
const int mod = 998244353;
using namespace std;
ll read(){
ll x = 0, f = 1;char ch = getchar();
while(ch > '9' || ch < '0'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
int n, m, p[maxn], root;
ll dp[maxn][maxn], sum[maxn];
int app[30][30];
int cnt;
char s[5];
void solve(){
n = read();
for(int i = 1; i <= n; i++){
scanf("%s", s + 1);
app[s[1] - 'a'][s[2] - 'a']++;
}
for(int i = 0; i < 26; i++){
for(int j = 0; j < 26; j++){
if(app[i][j]) p[++cnt] = app[i][j];
}
}
n = cnt;
for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + p[i];
for(int i = 1; i <= n; i++) dp[i][i] = p[i];
for(int len = 2; len <= n; len++){
for(int l = 1; l + len - 1 <= n; l++){
int r = l + len - 1;
dp[l][r] = 1e18;
for(int k = l; k <= r; k++){
dp[l][r] = min(dp[l][r], dp[l][k - 1] + dp[k + 1][r] + sum[r] - sum[l - 1]);
}
}
}
printf("%lld\n", dp[1][n]);
}
int main() {
solve();
return 0;
}
E2D-n元数:递归
一个正整数为 n n n 元数,当且仅当其各位数的 n n n 次方之和等于这个正整数本身。
例如 153 153 153 是一个 3 3 3 3 3 3 元数,因为 1 3 + 5 3 + 3 3 = 153 1^3+5^3+3^3=153 13+53+33=153。
给定正整数 n n n,请你求出所有 n n n 元数之和。 n ≤ 12 n\le12 n≤12
#include <cstdio>
#include <algorithm>
using namespace std;
using ll = long long;
int n;
ll pw[10], cnt[10], bit[10], ans;
int check(ll s) {
for (int i = 0; i < 10; i++)
bit[i] = 0;
while (s) {
bit[s % 10]++;
s /= 10;
}
for (int i = 0; i < 10; i++)
if (cnt[i] != bit[i])
return 0;
return 1;
}
void dfs(int c, ll s = 0, ll mn = 0, ll b = 1) {
if (mn > s || c < 0)
return;
ans += check(s) * s;
for (int i = 0; i <= c; i++) {
cnt[i]++;
dfs(i, s + pw[i], mn + max(b / 10, b * i), b * 10);
cnt[i]--;
}
}
void solve() {
scanf("%d", &n);
ans = 0;
for (int i = 0; i < 10; i++) {
pw[i] = 1;
for (int j = 1; j <= n; j++)
pw[i] *= i;
}
dfs(9);
printf("%lld\n", ans);
}
int main() {
int T;
scanf("%d", &T);
while (T--)
solve();
return 0;
}
E2F-超级计算机:维护优先队列
题目描述
有一台超级计算机,共有 n n n 块 CPU \text{CPU} CPU,这 n n n 块 CPU \text{CPU} CPU 按照 1 ∼ n 1 \sim n 1∼n 从小到大编号。
该计算机需要完成 m m m 个独立的任务,其会按顺序执行每一个任务。每一个任务会给出三个参数: t , w , s t, w, s t,w,s 。意为在 t t t 时刻将会下达该任务的计算指令,该次任务的权重为 w w w,此时空闲的编号最小的那块 CPU \text{CPU} CPU 将会执行本次计算任务(如果此刻没有空闲的 CPU \text{CPU} CPU 则不会执行该项任务的计算)。该块 CPU \text{CPU} CPU 进入忙碌状态,并在 t + s t + s t+s 时刻计算结束回归空闲状态。
请你计算出在全部执行完这 m m m 个任务后,每一块 CPU \text{CPU} CPU 上共运行了多少权重的任务。
输入
第一个行两个正整数 n n n 和 m m m,表示 CPU \text{CPU} CPU 的数量和任务的个数 ( 1 ≤ n , m ≤ 2 × 1 0 5 1 \le n,m \le 2\times 10^5 1≤n,m≤2×105)。
接下来 m m m 行,每行 3 3 3 个整数 t , w , s t, w, s t,w,s(保证当 i < j i < j i<j 时 t i ≤ t j t_i \le t_j ti≤tj,其中 0 ≤ t , w , s ≤ 1 0 9 0 \le t,w,s \le 10^9 0≤t,w,s≤109)。
输出
输出共 n n n 行,第 i i i 行表示在第 i i i 块 CPU \text{CPU} CPU 上运行任务的权重和。
输入样例
3 5 1 1 3 2 10 100 4 100 10000 10 1000 1000000000 100 1000000000 1 ```输出样例
101
10
1000样例解释 - 在 $t=1$ 时,全部 $\text{CPU}$ 处于空闲状态,因此第一块 $\text{CPU}$ 执行第一个任务,并在 $1 + 3 = 4$ 时刻回归空闲状态。 - 在 $t=2$ 时,第一块 $\text{CPU}$ 在忙碌状态,因此第二块 $\text{CPU}$ 执行第二个任务,并在 $2 + 100 = 102$ 时刻回归空闲状态。 - 在 $t=4$ 时,第一块 $\text{CPU}$ 已回归空闲状态,因此第三个任务由第一块 $\text{CPU}$ 执行。 - 在 $t=10$ 时,此时前两块 $\text{CPU}$ 均在忙碌状态,因此第四个任务由第三块 $\text{CPU}$ 执行。 - 在 $t=100$ 时三块 $CPU$ 均处于忙碌状态,因此第五项任务不执行。
#include<bits/stdc++.h>
using namespace std;
//#define abyss
typedef unsigned int UI;
typedef long long LL;
#define PLL pair<long long, long long>
#define MAXN 200005
priority_queue <PLL, vector<PLL>, greater <PLL> > line,
out;
LL ans[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) line.push({i, 0});
while (m--) {
LL T, W, S;
cin >> T >> W >> S;
while (!out.empty() && out.top().first <= T) {
line.push({out.top().second, out.top().first});
out.pop();
}
if (!line.empty()) {
auto top = line.top();
line.pop();
ans[top.first] += W;
out.push({T + S, top.first});
}
}
for (int i = 1; i <= n; i++) cout << ans[i] << "\n";
return 0;
}
E2J-右移拿金币:
题目描述
It’s behind you.
在无限长的一维数轴上会出现 n n n 枚金币,第 i i i 枚金币仅在时刻 t i t_i ti 出现在整点 x = x i x=x_i x=xi。你能拿到第 i i i 枚金币,当且仅当你在时刻 t i t_i ti 恰好位于 x = x i x=x_i x=xi。
初始时你在 x = 0 x=0 x=0,时刻为 t = 0 t=0 t=0。每个时刻你可以选择保持不动,或是沿坐标轴正方向移动一单位距离。也即如果你在时刻 t t t 位于 x = x 0 x=x_0 x=x0,则时刻 t + 1 t + 1 t+1 你必须位于 x 0 , x 0 + 1 x_0, x_0+1 x0,x0+1 之一。
你最多能拿到多少枚金币呢?
输入
本题测试点包含多组数据。
第一行,一个正整数 T T T( 1 ≤ T ≤ 10 1\leq T\leq 10 1≤T≤10),表示数据组数。
对于每组数据:
第一行,一个正整数 n n n( 1 ≤ n ≤ 2 × 1 0 5 1\leq n\leq 2\times 10^5 1≤n≤2×105),表示金币枚数。
接下来 n n n 行,每行两个整数 x i , t i x_i, t_i xi,ti( 1 ≤ x i ≤ 1 0 9 1\leq x_i \leq 10^9 1≤xi≤109, 1 ≤ t i ≤ 1 0 9 1\leq t_i\leq 10^9 1≤ti≤109),表示第 i i i 枚金币的位置与出现时间。
输出
对于每组数据:
输出一行,一个整数,表示最多能拿到的金币枚数。
#include <cstdio>
#include <algorithm>
using namespace std;
const int oo = 2e9;
const int N = 2e5 + 5;
int n;
int x[N], t[N];
int p[N], f[N];
int mx;
bool cmp(int a, int b) {
if (x[a] != x[b])
return x[a] < x[b];
return t[a] < t[b];
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &x[i], &t[i]);
t[i] -= x[i];
p[i] = i;
f[i] = oo;
}
sort(p + 1, p + n + 1, cmp);
mx = 0;
f[0] = 0;
for (int i = 1; i <= n; i++) {
int l = 0, r = mx;
int v = t[p[i]];
if (v < 0)
continue;
while (l < r) {
int mid = (l + r) / 2 + 1;
if (v < f[mid])
r = mid - 1;
else
l = mid;
}
mx = max(mx, r + 1);
f[r + 1] = v;
}
printf("%d\n", mx);
}
int main() {
int T;
scanf("%d", &T);
while (T--)
solve();
return 0;
}
如有版权问题,请联系作者删除/(ㄒoㄒ)/~~