【NOI2019】序列
题面
题解
orz AYSN
考场上秒出费用流64pts的奆神
我想这道题的时候在厕所,一开始想到费用流了,但是后来又想了一个28pts的背包,感觉背包更靠谱,于是就写背包了。。。
费用流貌似没那么难,可我却没有想到。果然还是太弱了。
用流的大小限制取的个数。
最左边 S,左侧 ABC 三个点,右侧 n 个点,左右边T。
从 l 到 k 枚举成对的点的个数 i,S 向 A 和 B 连流量 l−i,费用 0,向 C 连流量 i,费用 0。
A 向 n 个点连流量 1,费用 ai,类似的,B 连的费用是 bi,C是 ai+bi。
最后 n 个点向 T 连流量 1 费用 0。
update 2020 - 5 - 12
一篇随便的题解占据热搜,我有点愧疚呢,那就重新写一个 100 分做法吧!
说一下我在考场上的 28 分的暴力 dp 吧。
设 fi,j,k,l 为考虑到序列的第 i 个位置,已经选了 j 个 a 数组中的元素,k 个 b 数组中的元素,其中有 l 个位置上,a 数组的元素和 b 数组的元素同时被选上,这种情况下最大的选中的元素之和。
然后考虑这一次是
- 不选
- 只选 ai
- 只选 bi
- ai,bi 都选
状态 O(n4),转移 O(1),总复杂度 O(n4)。
事实上,O(n3) 也不难。这道题说是一个序列,但如果把 (ai,bi) 看做一个二元组,给定的 {ai},{bi} 一个二元组的集合。
对于一个集合,我们可以把集合中元素按某个关键字排序,使得 “集合” 的无序性得以体现,这样更接近本质。
我们按 ai 从大到小排序。考虑构成答案的元素的形式。显然,我们可以发现,若 i 位置没有选 a,一定不存在以后的一个 j>i,使得 j 只选了 a。如果这样,把 j 上的 a 换成 i 上的 a,答案不会变的更劣,都选的地方也不会变的更少。这样不仅决策少了,状态也少了。
所以前面一部分只能做出决策:
- 只选 a
- a,b 都选
后面一部分只能做出决策:
- 只选 b
- a,b 都选
- 都不选
前一部分只需要记录 fi,j,选到了第 i 个,有 j 个是 a,b 都选的。
后一部分只需要记录 gi,j,k,(从后往前)选到了第 i 个,有 j 个只选 b 的,k 个 a,b 都选的,显然有 (n−i+1−j−k) 个都不选的。
这样枚举前一部分和后一部分的分界,就可以做到 O(n3) 了。
到了 O(n2) 了。必须需要点灵感才能得到进一步的分数了。
我们发现,按 ai 或是 bi 排序是及其愚蠢的,因为我们不仅要枚举 a,还要枚举 b,还要算交集,这样似乎是怎样都无法做到 O(n2) 的。
我们按 ai+bi 排序。(自然,按 ai+bi 排序上一步也是可以做的,但这一步只能按 ai+bi 排序)
如果一个位置,既没有选 a,又没有选 b,那它以后肯定不会有都选的位置了。如果有,换过来,肯定不会变的更劣。
所以 ai+bi 肯定集中出现在一个前缀中(这个前缀不能有空的),我们枚举这个前缀,设为 i,此时,l 个都选的位置集中出现在前 i 个位置,可以知道前 i 个位置只空了 i−k 个位置,我们枚举空了 x 个 a,y 个 b,把最小的 x 个 a 和 y 个 b 去掉,再在剩下的序列中把缺的都选最大的加上,就可以了。
一个问题是如果同时去掉了一个位置上的 a 和 b,那该咋办?事实上,怎么办都可以,因为这样一定是劣的,不管它就行了。
为了保证复杂度 O(n2),我们用 4 个链表(分别对前面的 a,前面的 b,后面的 a,后面的 b 开)实现这些操作。添加复杂度 O(n),查询前 x 大 O(1)。
然后是 O(nlog2n)。这一步很显然,因为删的 a 是越来越大的,复活的 b 越来越小,新加的 a 越来越小,少的 b 越来越大,Δf(x) 单调减,这个函数关于 x 有凸性,我们只需要对找到第一个 Δf(x)<0 的位置,就是凸函数的最大值。
用平衡树或是树状数组维护,平衡树常数太大了,只能得 64 分。好像我写的树状数组也才得 80 分。
最后一步也很显然,我们对每一个 i ,把它取到最大值的点输出出来,发现从 i 到 i+1,要么位置不变,要么位置向右平移一位,所以直接判断一下就行了,复杂度 O(nlogn)!可以得到 100 分。
#include<bits/stdc++.h> #define N 200500 #define K (1<<19) #define ri register int #define LL long long using namespace std; int n,k,l,t[N],tru[1<<19]; LL ss[N],tt[N]; struct node { int a,b; bool operator < (const node &rhs) const { return tru[a]+tru[b]>tru[rhs.a]+tru[rhs.b]; } } v[N]; inline int read() { int ret=0,f=0; char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0' && ch<='9') ret*=10,ret+=ch-'0',ch=getchar(); return f?-ret:ret; } struct treap { int f[K],a[N],cnt; LL sum[K]; inline void clear() { for (ri i=1;i<=cnt;i++) { for (ri j=a[i];j<K;j+=j&(-j)) f[j]=sum[j]=0; } cnt=0; } inline void insert(ri x) { a[++cnt]=x; for (ri i=x;i<K;i+=(i&(-i))) f[i]++,sum[i]+=tru[x]; } inline void erase(ri x) { for (ri i=x;i<K;i+=(i&(-i))) f[i]--,sum[i]-=tru[x]; } inline LL findxsum(ri k) { register LL ret=0; register int cur=0; for (ri i=18;i>=0;i--) if (f[cur+(1<<i)]<=k ) { k-=f[cur+(1<<i)]; cur+=(1<<i); ret+=sum[cur]; } if (cur+1<K) ret+=k*1LL*tru[cur+1]; return ret; } inline LL finddsum(ri k) { register LL r1=0; register int tot=0; for (ri i=K-1;i;i-=(i&(-i))) r1+=sum[i],tot+=f[i]; return r1-findxsum(tot-k); } } t1,t2,t3,t4; inline LL solve(ri x,ri tot,ri i) { if (t[x]==i) return tt[x]; ri y=tot-x; t[x]=i; tt[x]=ss[i]-t1.findxsum(x)-t2.findxsum(y)+t3.finddsum(k-i+x)+t4.finddsum(k-i+y); return tt[x]; } inline LL dp() { sort(v+1,v+n+1); for (ri i=1;i<=n;i++) ss[i]=ss[i-1]+tru[v[i].a]+tru[v[i].b]; for (ri i=0;i<=n;i++) t[i]=-1; t1.clear(); t2.clear(); t3.clear(); t4.clear(); for (ri i=1;i<l;i++) t1.insert(v[i].a); for (ri i=l;i<=n;i++) t3.insert(v[i].a); for (ri i=1;i<l;i++) t2.insert(v[i].b); for (ri i=l;i<=n;i++) t4.insert(v[i].b); LL ans=0; for (ri i=l,pre=0;i<=n && i<=l+(k-l)+(k-l);i++) { t1.insert(v[i].a); t2.insert(v[i].b); t3.erase(v[i].a); t4.erase(v[i].b); int tot=i-l,lb=max(max(0,l+tot-k),l-i+tot),ret=lb,rb=min(min(tot-1,k-l-1),i-l-1); if (lb>rb+1) continue; if (pre<lb) pre=lb; if (pre+1<=rb+1 && solve(pre+1,tot,i)>=solve(pre,tot,i)) ret=pre+1; else ret=pre; LL cur=solve(ret,tot,i); pre=ret; if (cur>ans && lb<=rb+1) ans=cur; } return ans; } int main() { int T=read(); while (T--) { n=read(); k=read(); l=read(); int co=0; for (ri i=1;i<=n;i++) v[i].a=read(),tru[++co]=v[i].a; for (ri i=1;i<=n;i++) v[i].b=read(),tru[++co]=v[i].b; sort(tru,tru+co+1); int tn=unique(tru,tru+co+1)-tru-1; for (ri i=1;i<=n;i++) v[i].a=lower_bound(tru+1,tru+tn+1,v[i].a)-tru; for (ri i=1;i<=n;i++) v[i].b=lower_bound(tru+1,tru+tn+1,v[i].b)-tru; printf("%lld\n",dp()); } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· SQL Server统计信息更新会被阻塞或引起会话阻塞吗?
· C# 深度学习框架 TorchSharp 原生训练模型和图像识别
· 这或许是全网最全的 DeepSeek 使用指南,95% 的人都不知道的使用技巧(建议收藏)
· 拒绝繁忙!免费使用 deepseek-r1:671B 参数满血模型
· 本地搭建DeepSeek和知识库 Dify做智能体Agent(推荐)
· Sdcb Chats 重磅更新:深度集成 DeepSeek-R1,思维链让 AI 更透明!
· DeepSeek-R1本地部署如何选择适合你的版本?看这里