【简●解】巴厘岛的雕塑

【大意】#

给定AB,使一个区间的元素分为AxB组,每组的贡献为组内元素之和,最小化所有组贡献的或运算和。

【分析】#

开始一晃眼没看见区间连续这个条件,感觉做了假的题。。。

思考一个容易想到的dp式子,设f[i][j]表示前i个元素,分为j个组的最小化答案,考虑枚举转移点k,然后转移。。。。

Copy
#include <ctime> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #define ll long long using namespace std; const ll INF = 5e18; const int MAX = 2000 + 5; inline ll read() { ll res = 0; bool f = 0; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = 1; ch = getchar(); } while (ch <= '9' && ch >= '0') { res = (res << 3) + (res << 1) + ch - '0'; ch = getchar(); } return f ? (~ res + 1) : res; } ll n, A, B, Y[MAX], sum[MAX], f[MAX][MAX], ans = INF; int main() { n = read(), A = read(), B = read(); for (int i = 1;i <= n; ++i) for (int j = 0;j <= n; ++j) f[i][j] = INF; for (int i = 1;i <= n; ++i) Y[i] = read(), sum[i] = sum[i - 1] + Y[i], f[i][1] = sum[i]; for (int i = 1;i <= n; ++i) { int limit = min((ll)i, B); for (int j = 2;j <= limit; ++j) { for (int k = 1;k < i; ++k) { f[i][j] = min(f[i][j], f[k][j - 1] | (sum[i] - sum[k])); } } } for (int i = A;i <= B; ++i) ans = min(ans, f[n][i]); printf("%lld", ans); return 0; }

去提交。。啊!75Pt???TLE我还可以接受,还有的点WA

仔细思考了一下,发现这个式子并不满足无后效性。为什么?你选择前面的使它最小化了,但在或运算下,后面的影响你是不知道的。换句话说,本题中,若按之前的dp方程转移,局部最优解并不是全局最优解。

我们需要重新思考。

这个二进制运算和题目明显的提示,想到拆分考虑二进制数位,又因为最小化,所以贪心地从最高位开始对每一位进行考虑。

对于每一位pos,我们用f[i][j]表示用前i个元素,分成j组,能否使当前位为0(因为要最小化嘛)。

枚举转移点k,若f[k][j1]为真,且sum[k+1...i]在当前pos位上是0,那么就可以转移过来f[i][j]=1。然后累计ans

若存在f[n][i]=1(AiB)那么该位就可以等于0

再仔细思考,我们会发现若直接这样做,有可能会存在着低位的分组与高位的分组发生冲突。显然,我们应保留高位分组的情况,所以说,我们应保留之前已匹配好的位的状态,并且让转移也不能与这个状态发生冲突。

接着来思考子任务5

我们会发现它的数据有特殊性:A=1

怎么特殊?分组没有下限!

那我直接记录最少能用多少组让pos位可以填0不就行了?

g[i]表示前i位最少能分成多少组使当前位pos可以填0,那么同样的,只要满足转移条件,就有g[i]=min(g[i],g[k]+1)。其中除g[0]=0以外,其余g均为INF

然后就写了个分段dp

Copy
#include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #define ll long long #define Re register using namespace std; const int MAX = 2000 + 5; inline ll read() { ll res = 0; bool f = 0; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = 1; ch = getchar(); } while (ch <= '9' && ch >= '0') { res = (res << 3) + (res << 1) + ch - '0'; ch = getchar(); } return f ? (~ res + 1) : res; } ll Y[MAX], sum[MAX], f[MAX][MAX], ans; int n, A, B, g[MAX]; bool flag; inline void solution_1() { int limit = log2(sum[n]) + 1; for (int pos = limit; pos >= 0; --pos) { ll res = ans | ((1LL << pos) - 1LL); memset(g, 127, sizeof g); g[0] = 0; for (int i = 1;i <= n; ++i) { for (int k = 0;k < i; ++k) { if (((sum[i] - sum[k]) | res) == res) { g[i] = min(g[i], g[k] + 1); } } } if (g[n] > B) ans |= (1LL << pos); } } inline void solution_2() { int limit = log2(sum[n]) + 1; for (int pos = limit;pos >= 0; --pos) { ll res = ans | ((1LL << pos) - 1LL); memset(f, 0, sizeof f); f[0][0] = 1; flag = 0; for (int i = 1;i <= n; ++i) { int up = min(i, (int)B); for (int j = 1;j <= up; ++j) { for (int k = j - 1;k < i; ++k) { if (f[k][j - 1] && ((sum[i] - sum[k]) | res) == res) { f[i][j] = 1; } } } } for (int i = A;i <= B; ++i) if (f[n][i]) flag = 1; if (!flag) ans |= (1LL << pos); } } int main() { n = read(), A = read(), B = read(); for (int i = 1;i <= n; ++i) Y[i] = read(), sum[i] = sum[i - 1] + Y[i]; if (A == 1) solution_1(); else solution_2(); printf("%lld", ans); return 0; }
posted @   SilentEAG  阅读(238)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
CONTENTS