1 条题解
-
0
自动搬运
来自洛谷,原作者为

BreakPlus
empty should not be filled搬运于
2025-08-24 22:30:19,当前版本为作者最后更新于2024-06-20 22:20:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
一个好写 的做法。好像题解区都是分治?
考虑对于每个右端点计算答案。
众所周知,最大独立集问题是可以 DP 的。具体地,维护两个 DP 值 ,其中 表示最后一个数任意的最大独立集的值, 表示最后一个数必须不选的最大独立集的值。
若在末尾加入一个值为 的数,则转移如下:
这类问题有一个经典转化(类似 PKUSC2024 D1T3)。把转移式子写成以下形式:
发现每次新加入一个数, 的变化量与新的 均为 。这个值只和 有关。
也就是说,我们将答案进行拆贡献,每加入一个数,答案就增加 。这样,我们只需要时刻维护 的值,每次转移就 。于是可以完成对答案的计算。
我们从左往右扫,假设扫到 ,对每个 维护以 为左端点, 为右端点的区间的 的值。维护一个多重集 ,囊括所有的 。
记录 表示所有右端点为 的区间的最大独立集之和。每次移动 ,都相当于对 的 做了一次转移(右边新加一个 )。
实时维护 的变化量,即 ,记作 。其实这个值也就等于 。
如果不考虑要与 取 ,不难得到 ,也就是每一个 都变成 。
发现会有一些 小于 。我们直接从 的最大值开始判断,暴力将所有这样的 全部手动改成 。而其它的 在全局打 tag 完成修改(这个地方略有一点细节)。
手动改成 是否复杂度寄了?发现 值相同的可以捆绑在一起维护,所有手动修改的 都变成了 。
于是我们可以在 中维护二元组,记录 的值和这种值出现的次数。通过势能分析,我们只会往 里加入 个数,每次手动修改都会使 减小 。
具体实现的时候发现 时刻保持着有序,只会在头尾修改,拿个 deque 搞一下就是 的了。
ll n,a[100005],tx=1,ty=0,sum=0,ans=0,Ans=0; deque<P>S; // tx, ty 是全局 tag,S 中的一个数 x 其实已经变成了 tx * x + ty // tx 为 1 或 -1,所以序列有时候升序有时候降序,但时刻保持有序 ll decode(ll x){ return tx * x + ty; } // 还原真正的 x ll encode(ll x){ return (x - ty) / tx; } // 加入的时候需要进行逆操作 void solve(){ n=read(); for(ll i=1;i<=n;i++) a[i]=read(); for(ll i=1;i<=n;i++){ if(S.empty() || encode(0) <= S.front().fi) S.push_front(mkp(encode(0), 1)); else S.push_back(mkp(encode(0), 1)); tx=-tx; sum=mod-sum; ty=-ty+a[i]; sum=(sum+a[i]*i)%mod; // 全局修改 ll cnt = 0; while(!S.empty() && decode(S.front().fi) < 0){ sum=(sum+(-decode(S.front().fi))*(S.front().se))%mod; cnt+=S.front().se; S.pop_front(); } while(S.size() && decode(S.back().fi) < 0){ sum=(sum+(-decode(S.back().fi))*(S.back().se))%mod; cnt+=S.back().se; S.pop_back(); } // 对首尾的特殊修改 // sum 是 r[x] 的变化量 if(S.empty() || encode(0) <= S.front().fi) S.push_front(mkp(encode(0), cnt)); else S.push_back(mkp(encode(0), cnt)); _Add(ans, sum); // ans 是 r[x] _Add(Ans, ans); // Ans 是总的答案 } printf("%lld\n", Ans); }
- 1
信息
- ID
- 6644
- 时间
- 1000ms
- 内存
- 500MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者