w l s wls wls的民间数据[Link](2022蓝桥杯省赛C++ B组(民间数据) - 课程 - Daimayuan Online Judge)
模拟
答案 1478 1478 1478
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int g(int n) {
LL x = 1;
while (n --) {
x *= 9;
}
return x;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cout << 2 * g(3) + 2 * g(1) + 2 * g(0) << '\n';
return 0;
}
暴力枚举判断
答案: 0 , 1 , 2 0,1,2 0,1,2成立就是 14 14 14,不成立就是 4 4 4
不懂
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int mo[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
string str = "2022";
bool check(string str) {
for (int i = 0; i + 2 < str.size(); i ++) {
bool ok = true;
for (int j = i + 1; j <= i + 2; j ++) {
if ((int)(str[j - 1] - '0') + 1 != (int)(str[j] - '0'))
ok = false;
}
if (ok) return true;
}
return false;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int res = 0;
for (int i = 1; i <= 12; i ++) {
string m = to_string(i), d;
if (m.size() < 2) m = "0" + m;
for (int j = 1; j <= mo[i]; j ++) {
d = to_string(j);
if (d.size() < 2) d = "0" + d;
string t = str + m + d;
if (check(t)) {
res ++;
}
}
}
cout << res << '\n';
return 0;
}
循环节
a , b , n a,b,n a,b,n太大所以模拟行不通,七天一个循环节,所以 n = ( a × 5 + b × 2 ) × k + r n=(a\times 5+b\times 2)\times k + r n=(a×5+b×2)×k+r,所以看 n n n有几个循环节,最后再单独算一下 r r r的贡献即可。
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
LL a, b, n;
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> a >> b >> n;
LL d = (5 * a + 2 * b);
LL res = n / d * 7;
n %= d;
for (int i = 1; i <= 7; i ++) {
if (n <= 0) break;
if (i < 6) n -= a;
else n -= b;
res ++;
}
cout << res;
return 0;
}
贪心
对于每一个位置,最优解应该是把它砍掉以后往左走或往右走再回来的最大值,因为如果有一个位置 t t t开始走到当前位置是最优解,我从当前位置到 t t t会多出一截,所以从当前位置走是最优的。
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int a, b, n;
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) {
int d = max(i, n - i + 1);
cout << d + max(0, d - 2);
if (i != n) cout << '\n';
}
return 0;
}
贪心
这里有个很重要前提 A ≥ B A\ge B A≥B,假设第 i i i为的进制是 t i t_i ti, p i = ( t 1 × t 2 × . . . × t i − 1 ) p_i=(t_1\times t_2\times ...\times t_{i-1}) pi=(t1×t2×...×ti−1),我们的答案就是 ∑ i = 1 n ( A i − B i ) × p i \sum_{i=1}^n (A_i-B_i)\times p_i ∑i=1n(Ai−Bi)×pi,由于我们题目保证 A ≥ B A\ge B A≥B,如果 A ≠ B A\ne B A=B那么从高位到低位一定有某一位是 A i > B i A_i > B_i Ai>Bi的,进制合法所以不进位,因此高位具有决定性,所以我们只需要让所有位的进制都取最小合法的即可,即 m a x ( 2 , m i n ( A i , B i ) + 1 ) max(2,min(A_i,B_i) + 1) max(2,min(Ai,Bi)+1),单独每一位即可。
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int la, lb;
LL a[N], b[N], c[N], p[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
cin >> la;
for (int i = 1; i <= la; i ++)
cin >> c[i];
for (int i = 1; i <= la; i ++)
a[i] = c[la - i + 1];
cin >> lb;
for (int i = 1; i <= lb; i ++)
cin >> c[i];
for (int i = 1; i <= lb; i ++)
b[i] = c[lb - i + 1];
for (int i = 1; i <= max(la, lb); i ++)
p[i] = max(max(a[i], b[i]) + 1, 2ll);
LL res = 0;
LL pre = 1;
for (int i = 1; i <= max(la, lb); i ++) {
LL d = (a[i] - b[i]) * pre % mod;
res = (res + d) % mod;
pre = pre * p[i] % mod;
}
cout << (res % mod + mod) % mod;
return 0;
}
前缀和+双指针
首先很容易想到枚举正方形的两个对角点,然后二维前缀和把查区间值降到 O ( 1 ) O(1) O(1),枚举复杂度为 O ( n 4 ) O(n^4) O(n4),骗点分。
换一个方式来枚举,首先枚举上边和下边的位置,然后在这个子矩形里从左到右的算贡献,因为固定了上下边界,所以每一列相当于一个数(求和以后),就转化为在一个一维数组里有多少个连续的区间满足区间和 ≤ k \le k ≤k。
计数类问题先划分,我们可以以区间右端点在那来划分这些区间,对于一个区间 [ l , r ] [l,r] [l,r](以 r r r为右端点成立的最长的区间)当右端点右移的时候区间和会变大,它的左端点不会从 1 1 1开始只会从上个区间 l l l往右移动,因此具有单调性我们可以双指针 O ( n ) O(n) O(n)的维护,对于一个成立的最大区间 [ l , r ] [l,r] [l,r]的贡献为 r − l + 1 r-l+1 r−l+1(从右端点往左扩有这些区间)。
复杂度 O ( n 3 ) O(n^3) O(n3)
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 510, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int s[N][N];
int main() {
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++) {
scanf("%d", &s[i][j]);
s[i][j] += s[i - 1][j];
}
LL res = 0;
for (int l = 1; l <= n; l ++)
for (int r = l; r <= n; r ++) {
int sum = 0;
for (int i = 1, j = 1; i <= m; i ++) {
sum += s[r][i] - s[l - 1][i];
while (sum > k) {
sum -= s[r][j] - s[l - 1][j];
j ++;
}
res += i - j + 1;
}
}
printf("%lld\n", res);
return 0;
}
状压dp
考虑 f [ i ] [ j ] : 当 前 摆 到 第 i 列 且 占 了 i + 1 列 j 个 格 子 的 情 况 f[i][j]:当前摆到第i列且占了i+1列j个格子的情况 f[i][j]:当前摆到第i列且占了i+1列j个格子的情况
f [ i ] [ 0 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 2 ] f[i][0]=f[i-1][0]+f[i-1][2] f[i][0]=f[i−1][0]+f[i−1][2]
f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] × 2 + f [ i − 1 ] [ 1 ] f[i][1]=f[i-1][0]\times2+f[i-1][1] f[i][1]=f[i−1][0]×2+f[i−1][1]
f [ i ] [ 2 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] f[i][2]=f[i-1][0]+f[i-1][1] f[i][2]=f[i−1][0]+f[i−1][1]
为了不重复计算,初始我们让 f [ 1 ] [ 2 ] = 1 f[1][2]=1 f[1][2]=1表示上下摆放两个长条,并规定 f [ i ] [ 2 ] f[i][2] f[i][2]中由长条组成的情况只有上下摆放的情况,这里不算左右摆放长条是因为 f [ i ] [ 0 ] f[i][0] f[i][0]由 f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 2 ] f[i-1][0],f[i-1][2] f[i−1][0],f[i−1][2]转移过来从 f [ i − 1 ] [ 0 ] f[i-1][0] f[i−1][0]转移过了的情况存在最后并着两个长条的情况,因此不能再 f [ i − 1 ] [ 2 ] f[i-1][2] f[i−1][2]中计数。
由于只用前一维状态,可以滚动数组优化空间。
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e7 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1000000007;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
LL f[2][3];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
f[1][0] = 1, f[1][1] = 2, f[1][2] = 1;
for (int i = 2; i <= n; i ++) {
f[i&1][0] = (f[i-1&1][0] + f[i-1&1][2]) % mod;
f[i&1][1] = (f[i-1&1][0] * 2 + f[i-1&1][1]) % mod;
f[i&1][2] = (f[i-1&1][0] + f[i-1&1][1]) % mod;
}
cout << f[n&1][0] << '\n';
return 0;
}
线性dp
我的做法比较麻烦(不会状压dp的锅),考虑 f [ i ] [ j ] : 当 前 摆 到 第 i 列 且 往 第 i + 1 列 延 申 的 状 态 f[i][j]:当前摆到第i列且往第i+1列延申的状态 f[i][j]:当前摆到第i列且往第i+1列延申的状态
然后从前往后转移即可,一开始图省事开的 l o n g l o n g longlong longlong把空间炸了,寄,建议学滚动数组。
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e7 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1000000007;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int f[N][4];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
f[0][0] = 1;
cin >> n;
for (int i = 1; i <= n; i ++) {
f[i][3] = ((LL)f[i - 1][0] + f[i][3]) % mod;
f[i][0] = ((LL)f[i][0] + f[i - 1][0]) % mod;
if (i >= 1)
f[i][0] = ((LL)f[i][0] + f[i - 1][3]) % mod;
f[i][1] = ((LL)f[i][1] + f[i - 1][0]) % mod;
f[i][2] = ((LL)f[i][2] + f[i - 1][0]) % mod;
f[i][1] = ((LL)f[i][1] + f[i - 1][2]) % mod;
f[i][2] = ((LL)f[i][2] + f[i - 1][1]) % mod;
f[i + 1][0] = ((LL)f[i + 1][0] + f[i - 1][1] + f[i - 1][2]) % mod;
}
cout << (f[n][0] % mod + mod) % mod;
return 0;
}
枚举,搜索
比赛的时候读错题了,以为炮弹一发一发发射且求每一发最多炸多少, m d md md寄。
暴力的想法我们可以把搞一个数组判断这个雷炸没炸,每个炮弹暴力的判断是否让一些雷炸掉然后再从这个没有炸过的雷向外 d f s dfs dfs算贡献即可,瓶颈在于枚举所有的雷上,观察发现 1 ≤ r ≤ 10 1\le r\le 10 1≤r≤10,因为我们只需要枚举当前雷的半径内是否存在雷即可,即判断范围内的点 x , y x,y x,y是否为某个雷的圆心即可。
由于卡常,我们用 u n o r d e r e d _ m a p unordered\_map unordered_map来映射当前这个雷是否出现过,对于圆形相同的雷我们只需要记录它的最长半径和数量即可,然后暴力枚举范围内的点是否为雷是否可以继续往下搜即可。
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 5e4 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m, k;
struct Node {
int x, y, r, cnt;
}a[N];
bool st[N];
int idx, res;
unordered_map<LL, int> mp;
LL dist(int x1, int y1, int x2, int y2) {
return (LL)(x1 - x2) * (x1 - x2) + (LL)(y1 - y2) * (y1 - y2);
}
LL calc(int x, int y) {
return (LL)x * (1e9 + 1) + y;
}
void dfs(int u) {
res += a[u].cnt;
st[u] = true;
for (int x = a[u].x - a[u].r; x <= a[u].x + a[u].r; x ++) {
for (int y = a[u].y; dist(x, y, a[u].x, a[u].y) <= (LL)a[u].r * a[u].r; y ++) {
if (x >= 0 && x <= 1e9 && y <= 1e9) {
if (mp.find(calc(x, y)) != mp.end()) {
int id = mp[calc(x, y)];
if (!st[id])
dfs(id);
}
}
}
for (int y = a[u].y; dist(x, y, a[u].x, a[u].y) <= (LL)a[u].r * a[u].r; y --) {
if (x >= 0 && x <= 1e9 && y <= 1e9) {
if (mp.find(calc(x, y)) != mp.end()) {
int id = mp[calc(x, y)];
if (!st[id])
dfs(id);
}
}
}
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i ++) {
int x, y, r;
scanf("%d %d %d", &x, &y, &r);
if (mp.find(calc(x, y)) == mp.end())
mp[calc(x, y)] = ++idx, a[idx] = {x, y, r, 1};
else {
int id = mp[calc(x, y)];
a[id].r = max(a[id].r, r);
a[id].cnt ++;
}
}
for (int i = 1; i <= m; i ++) {
scanf("%d %d %d", &a[idx + 1].x, &a[idx + 1].y, &a[idx + 1].r);
dfs(idx + 1);
}
printf("%d\n", res);
return 0;
}
d p dp dp
考场上没动脑子,直接上了个二进制枚举暴力骗了点分。
由于 m ≤ 100 m\le 100 m≤100且 m m m每次只能减 1 1 1所以我们身上的酒不能超过当前的 m m m否则就喝不完了,考虑 f [ i ] [ j ] [ k ] : 当 前 路 过 i 个 店 , j 个 花 且 酒 还 剩 k 的 方 案 数 f[i][j][k]:当前路过i个店,j个花且酒还剩k的方案数 f[i][j][k]:当前路过i个店,j个花且酒还剩k的方案数。我们直接枚举方案数往后一个可以转移的状态转移即可,注意一下转移是否合法即可,不合法的情况有 1. 当 前 这 个 是 店 且 k × 2 > m 2. 当 前 这 个 是 花 且 k = = 0 3. 当 前 这 个 是 店 但 是 j = = m 1.当前这个是店且k\times 2>m\ \ 2.当前这个是花且k==0\ \ 3.当前这个是店但是j==m 1.当前这个是店且k×2>m 2.当前这个是花且k==0 3.当前这个是店但是j==m。
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int f[110][110][101];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m;
f[0][0][2] = 1;
for (int i = 0; i <= n; i ++)
for (int j = 0; j <= m; j ++)
for (int k = 0; k <= m; k ++) {
if (k * 2 <= m && j != m)
(f[i + 1][j][k * 2] += f[i][j][k]) %= mod;
if (k)
(f[i][j + 1][k - 1] += f[i][j][k]) %= mod;
}
cout << f[n][m][0] << '\n';
return 0;
}
贪心
它的这个开根的操作最多 6 , 7 6,7 6,7次就会把一个数开到 1 1 1,贪心的来看我们一定是让一些一样的尽量高一起被砍更优。对于每个竹子单独砍会有从它到 1 1 1的一些过程值,无非是砍到和它前一个的公共最长前缀一样还是和它后一个的公共最长前缀一样然后再一起砍掉,这个时候我就很蠢的写了区间 d p dp dp骗了点分就 r u n run run了。
继续往下贪心,对于当前位置:
如果和左边公共前缀更长,那么它就会砍一些变成左边的这个前缀
如果右边更长,我们其实是要变成右边的前缀然后然后再一起变成左边这个前缀,我们把这两步的贡献都计入当前砍成左边前缀
因此每一个点看看砍成左边最长前缀需要几步就可以了,特殊的第一个点它的前面是 1 1 1。
找前缀可以预处理然后暴力判断,我这里直接那个 m a p map map搞的,复杂度高一些,也可以接受。
#include
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
LL g(LL x) {
return sqrt(x / 2 + 1);
}
int dist(LL x, LL y) {
map<LL, bool> mp;
while (y > 1) {
mp[y] = true;
y = g(y);
}
int res = 0;
while (x > 1) {
if (mp[x]) {
break;
}
res ++;
x = g(x);
}
return res;
}
LL res;
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
LL pre = 1, res = 0;
for (int i = 1; i <= n; i ++) {
LL x; cin >> x;
res += dist(x, pre);
pre = x;
}
cout << res << '\n';
return 0;
}