题解:P16348 「MierOI R1」Eternal (Hard ver.)
NegetiveEdgeWeight · · 题解
我去我口胡出来了道啥?
约定集合
首先题目的意大概是说叫你找出数量最多的区间使得不存在两个区间
可以把
然后考虑若干个相交的区间(即建图后的连通块),一定是以下几种情况之一:
- 所有的区间共享一个左端点
j 。将这样的一个块称之为左倾块。 - 所有的区间共享一个右端点
i 。将这样的一个块称之为右倾块。 - 存在一个最大的区间
[j,i] ,使得所有其他的区间都被包裹再这个区间内部,并且这些区间有一部分以j 为左端点,一部分以i 为右端点。同时为了防止两组区间出现相交的情况存在一个隔离点k 使得不存在除了大区间的区间包含这个点。
定义
转移的时候对上面的三种情况分讨:
- 对于第一种情况,当前位置可以什么也不选,或者作为一个左倾块的结束。转移方程为:
f_i=\max_{1\le j<i}\Big(f_j+C(\{j\},[j,i]) \Big) - 对于第二种情况,可以选取若干个以
i 为右端点的区间。设选择的这些区间中,最靠左的左端点是j 。\ 那么可以在j 之前形成一个独立的最优解f_j ,然后加上所有左端点\ge j 且右端点为i 的区间数量。\ 那么转移方程为:f_i=\max_{j\in L_i}f_j+\text{C}([j,i],\{i\}) - 对于第三种情况,假设我们已经选出了一个隔离点
k ,那么转移方程就是
最终的
第一个转移方程可以使用线段树维护,第二个转移方程可以直接用我们的
观察
显然最优的
那么我们只需要在
但是这样子的时间复杂度仍然是
可以只遍历
然后再证明一下。
:::info[时间复杂度证明]
可以将每个区间
那么
所以我们启发式所产生的代价为
然后有个结论就是对于一个有
然后这个我不会证。qaq。可以上网搜搜。
:::
:::info[启发式正确性证明]
假设
那么对于集合中两个相邻的点
那么,这个区间的最大值一定是在这个区间的最左端。
同理如果
由于
所以取较小的就好了。 :::
最后按题解模拟即可。(什
:::success[Code]
#include<bits/stdc++.h>
#define For(a,b,c) for(int a=(int)(b);a<=(int)(c);a++)
#define Fro(a,b,c) for(int a=(int)(b);a>=(int)(c);a--)
#define Fov(g) for(auto e:g)
#define ms(a) memset(a,0,sizeof (a))
#define int long long
#define ull unsigned long long
#define pb push_back
using namespace std;
constexpr int N=2e5+5,MOD=998244353;
int cnte[N],f[N],tr[N<<2],n,mxm,mxv;
vector<int> L[N],R[N];
void pushUp(int id){
tr[id]=max(tr[id<<1],tr[id<<1|1]);
}
void build(int id,int l,int r){
tr[id]=0;
if(l==r) return;
int mid=l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}
void upd(int id,int l,int r,int pos,int val){
if(l==r){
tr[id]+=val;
return;
}
int mid=l+r>>1;
if(pos<=mid)
upd(id<<1,l,mid,pos,val);
else
upd(id<<1|1,mid+1,r,pos,val);
pushUp(id);
}
int query(int id,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return tr[id];
int mid=l+r>>1,ans=0;
if(ql<=mid)
ans=max(ans,query(id<<1,l,mid,ql,qr));
if(qr>mid)
ans=max(ans,query(id<<1|1,mid+1,r,ql,qr));
return ans;
}
void eval(int k,int j,int i){
if(k<j+1||k>i) return;
int pj=upper_bound(R[j].begin(),R[j].end(),min(k,i-1))-R[j].begin();
int si=L[i].end()-lower_bound(L[i].begin(),L[i].end(),k);
mxv=max(mxv,pj+si);
}
void solve(){
mxm=0;
cin>>n;
For(i,1,n){
int l,r;
cin>>l>>r;
if(l==r)
cnte[l]++;
else
L[r].pb(l),R[l].pb(r);
mxm=max(mxm,r);
}
For(i,1,mxm)
sort(L[i].begin(),L[i].end()),sort(R[i].begin(),R[i].end());
build(1,1,mxm);
f[0]=0;
For(i,1,mxm){
Fov(L[i])
upd(1,1,mxm,e,1);
int nfi=f[i-1];
if(i>1)
nfi=max(nfi,query(1,1,mxm,1,i-1));
For(m,0,(int)L[i].size()-1)
nfi=max((ull)nfi,f[L[i][m]]+L[i].size()-m);
For(m,0,(int)L[i].size()-1){
int j=L[i][m];
if(m>0&&j==L[i][m-1])
continue;
int cji=upper_bound(L[i].begin(),L[i].end(),j)-L[i].begin()-m;
mxv=0;
if(R[j].size()<L[i].size()){
eval(j+1,j,i);
Fov(R[j])
if(e>=j+1&&e<=i-1)
eval(e,j,i);
}
else{
eval(i,j,i);
Fov(L[i])
if(e>=j+1&&e<=i)
eval(e,j,i);
}
nfi=max(nfi,f[j]+cji+mxv);
}
f[i]=nfi+cnte[i];
upd(1,1,mxm,i,f[i]);
}
cout<<f[mxm]<<"\n";
For(i,0,mxm)
cnte[i]=0,L[i].clear(),R[i].clear(),f[i]=0;
}
signed main(){
int t;
cin>>t;
while(t--)
solve();
return 0;
}
:::