Atcoder [ABC410G] Longest Chord Chain 题解 [ 蓝 ] [ 线段树优化 DP ]
Longest Chord Chain:又简单又典的题。
首先断环为链,把所有弦放在一根直线上考虑,这是显然的。
那么两条弦相交,当且仅当他们在直线上不是相互独立或者相互包含的关系。
接下来考虑转化选择的限制:
- 所有弦不能相交 \(\to\) 直线上的关系只能是相互独立或者相互包含。
- 选择一根线穿过选择的弦 \(\to\) 一条弦不能同时穿过相互独立的另外两条弦,所以相当于只能穿过相互包含的弦。
因此,问题被转化为直线上最多有多少条弦是相互嵌套、包含的关系。
于是设计线性 DP:\(dp_i\) 表示以 \(i\) 为最外侧右端点的答案。因为所有弦的端点互不相同,所以每个点都只用转移一次。假设前驱端点为 \(pre\),那么只要查询最外层左端点在 \([pre+1,i-1]\) 之间的最大值就可以转移了。这是个二维偏序问题,而又因为我们的 \(i\) 是从小到大枚举的,上界限制显然满足,所以直接查询最外层左端点大于 \(pre\) 的最大值即可。采用线段树或树状数组实现,时间复杂度 \(O(n\log n)\)。
或者还可以把它转化为 LIS 问题:将相互包含的弦按左端点排序,右端点的 LIS 即为答案。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=800005;
int n,pre[N],dp[N],ans;
struct Node{
int l,r,v;
};
struct Segtree{
Node tr[4*N];
void pushup(int p)
{
tr[p].v=max(tr[lc].v,tr[rc].v);
}
void build(int p,int ln,int rn)
{
tr[p]={ln,rn,0};
if(ln==rn)return;
int mid=(ln+rn)>>1;
build(lc,ln,mid);
build(rc,mid+1,rn);
pushup(p);
}
void update(int p,int x,int v)
{
if(tr[p].l==x&&tr[p].r==x)
{
tr[p].v=v;
return;
}
int mid=(tr[p].l+tr[p].r)>>1;
if(x<=mid)update(lc,x,v);
else update(rc,x,v);
pushup(p);
}
int query(int p,int ln,int rn)
{
if(ln<=tr[p].l&&tr[p].r<=rn)return tr[p].v;
int mid=(tr[p].l+tr[p].r)>>1;
if(rn<=mid)return query(lc,ln,rn);
if(ln>=mid+1)return query(rc,ln,rn);
return max(query(lc,ln,rn),query(rc,ln,rn));
}
}tr1;
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
int a,b;
cin>>a>>b;
if(a>b)swap(a,b);
pre[b]=a;
pre[b+2*n]=a+2*n;
pre[a+2*n]=b;
}
tr1.build(1,1,4*n);
for(int i=1;i<=4*n;i++)
{
if(pre[i]==0)continue;
dp[i]=tr1.query(1,pre[i]+1,i)+1;
tr1.update(1,pre[i],dp[i]);
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号