【题解】「JOISC 2017 Day 2」门票安排

solution:
不看题解做不出来
考虑翻转一些区间使得答案更优。(这里不讨论 [l,r] = ∅ \empty 的情况)
设 a_i 表示翻转前的答案, b_i 表示翻转后的答案。

性质一:任意两个翻转的区间一定有交集。
证明:考虑反证。假设有两个翻转区间交集为空,那么翻转前肯定比翻转后更优。

性质二:设所有翻转区间交集为 [l,r] ,其中 b_t=max(a_l,…,a_r) ,l<=t<=r 。则满足 b_t>=max(b_i)-1 。
证明:当 l<=t’<=r 时显然成立 。否则任意撤销一个翻转区间,答案不会更劣。

性质三:t 满足 a_t=max(a_i)
证明:当 l<=t’<=r 时显然成立。考虑反证,假设 a_t<a_t’ ,用 a_t-b_t 表示经过 t 的翻转区间的个数, 则有 a_t-b_t>=a_t’-b_t’+1 (否则 t’ 也是公共点),即 a_t+b_t’>=b_t+a_t’+1 ,这与性质二和假设矛盾。

性质四:对于任意满足 a_t=max(a_i) 的点 ,一定满足 t\in [l,r]
证明:和性质三类似。

现在我们考虑算法流程:

首先二分答案 w ,为了便于确定一个点的最终状态,我们还需要得到翻转区间的个数 cnt=a[t]-mid 或 cnt=a[t]-mid+1 。

贪心算法流程如下:

  1. 求出最左边的满足 a_t=max(a_i) 的点,作为我们的 t (t\in [l,r])
  2. 从左往右扫描线,将左端点对应的塞进 r 的优先队列
  3. 设 p 表示当前已经选择的翻转区间的个数,判断 a[i]-p+cnt-p > w 是否满足;
  4. 最后可以证明 p=cnt 。再扫描一遍 [t+1,n] 是否超过 w ,可以差分数组维护

时间复杂度 O(nlog^2n) 。

总结:思路并不复杂,但是结论很难想,也比较抽象。

#include<bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f #define fi first #define se second #define All(a) a.begin(),a.end() using namespace std; const int Maxn=2e5+5; inline int read() { int X=0; bool flag=1; char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();} while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();} if(flag) return X; return ~(X-1); } int n,m; ll a[Maxn],d[Maxn],cf[Maxn]; vector<int> vec[Maxn]; struct node{ int l,r,c; }q[Maxn]; priority_queue<pair<int,int>> Q; bool check(ll w,int t,ll cnt) { for(int i=1;i<=n;i++) cf[i]=0,d[i]=a[i]; for(int i=1;i<=n;i++) { vec[i].clear(); } while(Q.size()) Q.pop(); for(int i=1;i<=m;i++) { if(q[i].l<=t&&t<=q[i].r) { vec[q[i].l].push_back(i); } } ll p=0; for(int i=1;i<=t;i++) { for(auto y:vec[i]) { Q.push(make_pair(q[y].r,q[y].c)); } //p: 当前已经选择的翻转区间次数 //a[i]-p : 现在 a[i] 的实际覆盖次数 //cnt-p : 剩下的翻转中 l\nin[1,i] 时 a[i] 的最终覆盖次数 while(a[i]-p+cnt-p>w) { if(!Q.size()) return 0; int pr=Q.top().fi; ll c=Q.top().se; Q.pop(); ll ps=min(c,(a[i]-p+cnt-p-w+1)/2); p+=ps; cf[pr]+=2*ps; if(ps!=c) Q.push(make_pair(pr,c-ps)); } } assert(p==cnt); cf[t]-=cnt; for(int i=t+1;i<=n;i++) { cf[i]+=cf[i-1]; if(cf[i]+a[i]>w) return 0; } return 1; } signed main() { n=read(),m=read(); for(int i=1;i<=m;i++) { q[i].l=read(),q[i].r=read(),q[i].c=read(); if(q[i].l>q[i].r) swap(q[i].l,q[i].r); a[q[i].l]+=q[i].c; a[q[i].r]-=q[i].c; } int tl,tr; ll tmax=0; for(int i=1;i<=n;i++) { a[i]+=a[i-1]; tmax=max(tmax,a[i]); } for(int i=1;i<=n;i++) { if(a[i]==tmax) { tr=i; } } for(int i=n;i>=1;i--) { if(a[i]==tmax){ tl=i; } } ll l=1,r=a[tl],res=0; while(l<=r) { ll mid=l+r>>1; if(check(mid,tl,a[tl]-mid)||check(mid,tl,a[tl]-mid+1)) res=mid,r=mid-1; else l=mid+1; } printf("%lld",res); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530256.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(19)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示