日常刷题2025-3-8
日常刷题2025-3-8
A进制
通过率:12%
2025钉耙编程热身赛第1009题
思路:DP
思路:时间为8s,所以可以考虑暴力做法,我们会发现这三种操作不涉及进位也就是说我们可以先预处理出前面的n-1位在这三种操作下的所得结果,再加上这一位,例如\(yu[i][j]=yu[i/a][j/a]*a+min(i\%a,j\%a)\),表示i和j进行与运算所得结果,i,j的范围为0~1023,超过的部分用递归进行求解,然后我们定义\(bool dp[i][j]\)为第i次操作能得到j,所以我们只需用第i-1次操作得到的每个数与ai进行三次运算即可,最后遍历\(dp[n][i]\)求和,即得到最终结果。
代码
#include <bits/stdc++.h>
typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1024;
void solve(){
int n, a, x;
std::cin >> n >> a >> x;
std::vector<std::vector<int>> yu(N,std::vector<int>(N));
std::vector<std::vector<int>> hy(N,std::vector<int>(N));
std::vector<std::vector<int>> yh(N,std::vector<int>(N));
for(int i=0;i<=1023;i++)
{
for(int j=0;j<=1023;j++)
{
yu[i][j]=yu[i/a][j/a]*a+std::min(i%a,j%a);
hy[i][j]=hy[i/a][j/a]*a+std::max(i%a,j%a);
yh[i][j]=yh[i/a][j/a]*a+(i%a+j%a)%a;
}
}
auto pyu = [&](auto self, int i, int j)->int{
if (i < N && j < N) return yu[i][j];
else return self(self, i/a, j/a)*a+std::min(i%a,j%a);
};
auto phy = [&](auto self, int i, int j)->int{
if (i < N && j < N) return hy[i][j];
else return self(self, i/a, j/a)*a+std::max(i%a,j%a);
};
auto pyh = [&](auto self, int i, int j)->int{
if (i < N && j < N) return yh[i][j];
else return self(self, i/a, j/a)*a+(i%a+j%a)%a;
};
std::vector dp(n+1, std::vector<bool>(40005));
dp[0][x] = true;
for (int i = 1; i <= n; i++){
int z; std::cin >> z;
for (int j = 0; j < 40005; j++){
if (!dp[i-1][j]) continue;
dp[i][pyu(pyu, j, z)] = true;
dp[i][phy(phy, j, z)] = true;
dp[i][pyh(pyh, j, z)] = true;
}
}
int sum = 0;
for (int i = 0; i < 40005; i++){
if (dp[n][i]) sum += i;
}
std::cout << sum << '\n';
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
int t = 1, i;
std::cin >> t;
for (i = 0; i < t; i++){
solve();
}
return 0;
}
P1972 [SDOI2009] HH的项链
紫色
思路
题目:询问题,每次询问一个区间包含多少中数。
并不是区间计数问题,双指针排除。那么基本就是线段树或者树状数组。感觉信息不像是可差分的。
所以优先考虑线段树。
正解:树状数组+离线
看洛谷题解
代码
#include <bits/stdc++.h>
typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1000010;
bool cmp(const std::array<int, 3> &a, const std::array<int, 3> &b){
return a[1] < b[1];
}
void solve(){
int n, m;
std::cin >> n;
std::vector<int> a(n+1);
for (int i = 1; i <= n; i++) std::cin >> a[i];
std::cin >> m;
std::vector<std::array<int, 3>> b(m+1);
for (int i = 1; i <= m; i++){
int l, r;
std::cin >> l >> r;
b[i] = {l, r, i};
}
std::sort(b.begin()+1, b.end(), cmp);
// for (int i = 1; i <= m; i++){
// std::cout << b[i][0] << ' ' << b[i][1] << '\n';
// }
std::vector<int> fenw(n+1);
auto add = [&](int x, int k)->void{
for (; x<=n; x += x & -x) fenw[x] += k;
};
auto ask = [&](int x)->int{
int res = 0;
for (; x; x -= x & -x) res += fenw[x];
return res;
};
std::vector<int> ans(m+1);
std::vector<int> vis(N);
int j = 1;
for (int i = 1; i <= m; i++){
for (int k = j; k <= b[i][1]; k++){
if (vis[a[k]]) add(vis[a[k]], -1);
add(k, 1);
vis[a[k]] = k;
}
j = b[i][1] + 1;
int res = ask(b[i][1]) - ask(b[i][0]-1);
ans[b[i][2]] = res;
}
for (int i = 1; i <= m; i++) {
std::cout << ans[i] << '\n';
}
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
int t = 1, i;
for (i = 0; i < t; i++){
solve();
}
return 0;
}
P1471 方差
紫色
正解:线段树维护区间和,区间平方和
看洛谷解析
经验+1
题目中直接出现的或者间接出现的式子,一定要拆一拆。
代码
#include <bits/stdc++.h>
typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;
#define lson (rt << 1)
#define rson (rt << 1 | 1)
double a[100005]; //用来构建线段树的数组
struct node {
double v, mul, lz;
}tr[100005 << 2];
// 区间合并
node merge(node x, node y) {
double sum = x.v + y.v;
double mul = x.mul+y.mul;
return {sum, mul, 0};
}
// 把懒信息下发下去
// 有懒标记就下发
void push_down(int rt, int l, int r) {
if (tr[rt].lz) {
int mid = l + r >> 1;
double x = tr[rt].lz;
tr[lson].lz += x;
tr[rson].lz += x;
tr[lson].mul += 2*x*tr[lson].v+x*x*(mid-l+1);
tr[rson].mul += 2*x*tr[rson].v + (r-mid)*x*x;
tr[lson].v += (mid - l + 1) * x;
tr[rson].v += (r - mid) * x;
tr[rt].lz = 0;
}
}
// 从l~r建树,存在tr数组中的rt位置
void build(int rt, int l, int r) {
if (l == r) {
tr[rt].v = a[l];
tr[rt].mul = a[l]*a[l];
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
tr[rt] = merge(tr[lson], tr[rson]);
}
// 区间更新 l, r 默认是 1 和 n , L 和 R 是我们要更新的区间
void update(int rt, int l, int r, int L, int R, double x) {
if (l >= L && r <= R) {
tr[rt].lz += x;
tr[rt].mul += 2*x*tr[rt].v+(r-l+1)*x*x;
tr[rt].v += 1ll * (r - l + 1) * x;
return;
}
// 从这往下的代码都是不需要动的
push_down(rt, l, r);
int mid = l + r >> 1;
if (mid >= L)
update(lson, l, mid, L, R, x);
if (mid < R) {
update(rson, mid + 1, r, L, R, x);
}
tr[rt] = merge(tr[lson], tr[rson]);
}
// 区间查询
node query(int rt, int l, int r, int L, int R) {
if (l >= L && r <= R) {
return tr[rt];
}
push_down(rt, l, r);
node res = {0, 0, 0};
int mid = l + r >> 1;
if (mid >= L) {
res = merge(res, query(lson, l, mid, L, R));
}
if (mid < R){
res = merge(res, query(rson, mid + 1, r, L, R));
}
return res;
}
void solve(){
int n, m;
std::cin >> n >> m;
for (int i = 1; i <= n; i++) std::cin >> a[i];
build(1, 1, n);
while (m--){
int ty; std::cin >> ty;
if (ty == 1){
int x, y;
double k;
std::cin >> x >> y >> k;
update(1, 1, n, x, y, k);
}else if (ty == 2){
int x, y; std::cin >> x >> y;
double res = query(1, 1, n, x, y).v;
std::cout << res / (y-x+1) << '\n';
}else {
int x, y; std::cin >> x >> y;
node res = query(1, 1, n, x, y);
double avg = res.v / (y-x+1);
double sq_sum = res.mul;
double variance = sq_sum/(y-x+1) - avg * avg;
std::cout << variance << '\n';
}
}
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(4);
int t = 1, i;
for (i = 0; i < t; i++){
solve();
}
return 0;
}
P4513 小白逛公园
紫色
正解:线段树维护最大字段和
看洛谷题解
经验+1
- 无懒标记的线段树适用于单点更新和区间查询的场景,但在区间更新时效率较低。
- 有懒标记的线段树适用于需要频繁进行区间更新的场景,能够显著提高效率。
无懒标记线段树区间更新是\(O(n)\)的
有懒标记线段树区间更新是\(O(logn)\)的
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#define lson (rt << 1)
#define rson (rt << 1 | 1)
int a[500005]; //用来构建线段树的数组
struct node {
int maxv , maxl , maxr , sumv ;
}tr[500005 << 2];
// 区间合并
node merge(node x, node y) {
node res = {-1000, -1000, -1000, 0};
res.sumv = x.sumv + y.sumv;
if (x.maxr < 0 && y.maxl < 0){
res.maxv = std::max(x.maxr, y.maxl);
}else{
res.maxv = 0;
if (x.maxr > 0) res.maxv += x.maxr;
if (y.maxl > 0) res.maxv += y.maxl;
}
res.maxv = std::max(x.maxv, res.maxv);
res.maxv = std::max(y.maxv, res.maxv);
res.maxl = std::max(x.maxl, x.sumv+y.maxl);
res.maxr = std::max(y.maxr, y.sumv+x.maxr);
return res;
}
// 从l~r建树,存在tr数组中的rt位置
void build(int rt, int l, int r) {
if (l == r) {
tr[rt].maxv = tr[rt].maxl = tr[rt].maxr = tr[rt].sumv = a[l];
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
tr[rt] = merge(tr[lson], tr[rson]);
}
// 区间更新 l, r 默认是 1 和 n , L 和 R 是我们要更新的区间
void update(int rt, int l, int r, int L, int R, int x) {
if (l >= L && r <= R) {
tr[rt].maxv = tr[rt].maxl = tr[rt].maxr = tr[rt].sumv = x;
return;
}
// 从这往下的代码都是不需要动的
// push_down(rt, l, r);
int mid = (l + r) >> 1;
if (mid >= L)
update(lson, l, mid, L, R, x);
if (mid < R) {
update(rson, mid + 1, r, L, R, x);
}
tr[rt] = merge(tr[lson], tr[rson]);
}
// 区间查询
node query(int rt, int l, int r, int L, int R) {
if (l >= L && r <= R) {
return tr[rt];
}
// push_down(rt, l, r);
node res = {-1000, -1000, -1000, 0};
int mid = (l + r) >> 1;
if (mid >= L) {
res = merge(res, query(lson, l, mid, L, R));
}
if (mid < R){
res = merge(res, query(rson, mid + 1, r, L, R));
}
return res;
}
void solve(){
int n, m;
std::cin >> n >> m;
for (int i = 1; i <= n; i++) std::cin >> a[i];
build(1, 1, n);
while (m--){
int ty; std::cin >> ty;
if (ty == 1){
int x, y; std::cin >> x >> y;
if (x > y) std::swap(x, y);
int res = query(1, 1, n, x, y).maxv;
std::cout << res << '\n';
}else{
int x, y;
std::cin >> x >> y;
update(1, 1, n, x, x, y);
}
}
}
signed main()
{
int t = 1, i;
for (i = 0; i < t; i++){
solve();
}
return 0;
}
D - Minimum XOR Path
思路:dfs回溯
首先看一下数据范围,节点最多才10个,边也非常少。所以肯定是一个比较暴力的做法。题目让求能到达终点的路径上的标签的异或值,所以直接考虑暴搜出所有路径,暴搜路径是一个回溯的过程。
既然是回溯,那么就要小心不要更改某个状态的属性。
代码
#include <bits/stdc++.h>
typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;
void solve(){
int n, m; std::cin >> n >> m;
std::vector go(n+1, std::vector<int>());
std::vector d(n+1, std::vector<i64>(n+1));
for (int i = 0; i < m; i++){
i64 u, v, w; std::cin >> u >> v >> w;
go[u].push_back(v);
go[v].push_back(u);
d[u][v] = w;
d[v][u] = w;
}
i64 ans = LONG_LONG_MAX;
std::vector<bool> vis(n+1);
auto dfs = [&](auto self, int cur, i64 p)->void{
if (cur == n) {
ans = std::min(ans, p);
return;
}
vis[cur] = 1;
for (auto to : go[cur]){
if (vis[to]) continue;
self(self, to, p^d[cur][to]);
}
vis[cur] = 0;
};
dfs(dfs, 1, 0);
std::cout << ans << '\n';
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
int t = 1, i;
for (i = 0; i < t; i++){
solve();
}
return 0;
}

浙公网安备 33010602011771号