CDOJ 1292 卿学姐种花(分块)

题目链接:点击打开链接

思路:

由于是一个区间更新问题, 而且更新的值不一样, 所以我们考虑分块。  对于一个块, 我们维护第i块的第一个元素被加了多少了sum[i],第i块被更新了多少次cnt[i], 那么对于一个块内, 元素依次增加sum[i]递减cnt[i], 这是一个等差数列。

细节参见代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <ctime>
#include <bitset>
#include <cstdlib>
#include <cmath>
#include <set>
#include <list>
#include <deque>
#include <map>
#include <queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef long double ld;
const double eps = 1e-6;
const double PI = acos(-1);
const int mod = 772002 + 233;
const int INF = 0x3f3f3f3f;
const int seed = 131;
const ll INF64 = ll(1e18);
const int maxn = 1e4 + 10;
int T,n,m;
int belong[maxn];
int block;
int num;
int r[maxn], l[maxn];
ll sum[maxn];
ll cnt[maxn];
ll a[maxn];
void build() {
    block = sqrt(n);
    num = n / block;
    if(n % block) num++;
    for(int i = 1; i <= num; i++) {
        l[i] = (i-1)*block + 1;
        r[i] = i * block;
        sum[i] = 0;
        cnt[i] = 0;
    }
    r[num] = n;
    for(int i = 1; i <= n; i++) belong[i] = (i-1)/block+1;
}
void update(int x, int y, int v) {
    if(belong[x] == belong[y]) {
        for(int i = x; i <= y; i++) a[i] += v--;
    }
    else {
        for(int i = x; i <= r[belong[x]]; i++) a[i] += v--;
        for(int i = belong[x]+1; i < belong[y]; i++) {
            sum[i] += v;
            cnt[i]++;
            v -= block;
        }
        for(int i = l[belong[y]]; i <= y; i++) a[i] += v--;
    }
}
int query(int x) {
    ll ans = 0, id = belong[x];
    ans = sum[id] - cnt[id]*(x-l[id]);
    if(ans < 0) ans = 0;
    return (ans + a[x]) % mod;
}
int q;
int main() {
    scanf("%d%d", &n, &q);
    build();
    for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    while(q--) {
        int id; scanf("%d", &id);
        if(id == 1) {
            int x, y; scanf("%d%d", &x, &y);
            int r = min(x+y-1, n);
            update(x, r, y);
        }
        else {
            int x;
            scanf("%d", &x);
            printf("%d\n", query(x));
        }
    }
    return 0;
}


### CDOJ 1805 矩阵 题解 #### 问题分析 题目描述了一个 \( N \times M \) 的矩阵,其中每个元素要么是 \( 1 \),要么是 \( -1 \)[^1]。对于每一行 \( A_i \) 每一列 \( B_j \),定义它们分别为该行该列所有元素的乘积。目标是计算满足条件的所有不同矩阵的数量,即所有的 \( A_i, B_j \) 均等于给定值 \( K \)(\( K = 1 \) 或者 \( K = -1 \))[^1]。 此问题的核心在于如何通过动态规划或其他方法高效地枚举并统计符合条件的矩阵数量。 --- #### 动态规划思路 为了求解这个问题,可以采用动态规划的思想来优化时间复杂度。以下是具体的实现逻辑: 1. **状态定义** 定义 `dp[i][j]` 表示构建一个大小为 \( i \times j \) 的矩阵,并使每行每列的乘积均为 \( K \) 所需的不同方案数[^2]。 2. **初始化** 对于单行或多列的情况,显然只有一种方式能够满足条件(全填入 \( 1 \)),因此有: \[ dp[1][j] = dp[i][1] = 1 \quad (i, j \geq 1) \] 3. **转移方程** 转移关系基于子问题分解: - 如果当前矩阵的高度大于宽度 (\( i > j \)),则可以通过减少高度的方式继承更小子问题的结果; \[ dp[i][j] = dp[i-j][j] + dp[i][j-1] \] - 若两者相等,则额外增加一种新的可能性; \[ dp[i][i] = 1 + dp[i][i-1] \] - 否则保持不变: \[ dp[i][j] = dp[i][i] \] 4. **最终结果提取** 输入整数 \( n \),表示需要解决的是 \( n \times n \) 大小的正方形矩阵情况下的计数值,故只需返回 `dp[n][n]` 即可完成任务。 --- #### 实现代码 下面是完整的程序实现,采用了上述提到的状态转换规则以及边界处理机制: ```cpp #include <iostream> using namespace std; #define MAXN 121 int dp[MAXN][MAXN] = {0}; // 初始化 DP 数组 void init_dp() { int i, j; for (i = 1; i <= 120; ++i) { dp[1][i] = dp[i][1] = 1; // 初始条件设置 } for (i = 2; i <= 120; ++i) { for (j = 2; j <= 120; ++j) { if (i < j) { dp[i][j] = dp[i][i]; } else if (i == j) { dp[i][j] = 1 + dp[i][j - 1]; } else if (i > j) { dp[i][j] = dp[i - j][j] + dp[i][j - 1]; } } } } int main() { init_dp(); // 进行动态规划预处理 int n; while (cin >> n && n >= 1) { cout << dp[n][n] << endl; // 输出对应规模下矩阵数目 } return 0; } ``` --- #### 结果解释 运行以上代码后,输入任意自然数 \( n \),即可得到对应的 \( n \times n \) 尺寸矩阵中符合要求的总数目。注意这里假定了行列长度一致的情形;如果扩展到一般矩形区域,则还需进一步调整算法框架适应变化范围内的参数设定。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值