题解 P3365 【改造二叉树】

Boeing737_MAX_8

2019-08-18 16:46:23

Solution

``` 20191013修改:个人博客网址变更 ``` 同样欢迎来到[我的个人博客查看](https://officerbonin.home.blog/2019/08/19/%e3%80%90%e4%bf%a1%e7%ab%9e%e3%80%91luogup3365-%e6%94%b9%e9%80%a0%e4%ba%8c%e5%8f%89%e6%a0%91/) 本题被教练选作了模拟赛的第一题,~~结果我去死磕树形DP,噗....~~ 考后[一位大佬](https://www.luogu.org/space/show?uid=88268)给我讲了本题的解法。 思想等同于本题[张亦弛给出的题解](https://www.luogu.org/blog/1557114139--com/solution-p3365)。 我发这篇题解,因为我使用的线段树优化的方式,但瞧见其他题解都是二分优化的,没有题解用这个方式。 修改后交题解没过,那就加点解释吧,见代码. ```cpp #include<iostream> #include<cstdio> #include<cstring> #include<climits> #include<cmath> #include<algorithm> #include<map> #include<vector> #define maxn 100005 using namespace std; int n; struct node { int lc,rc,fa; } pot[maxn]; int a[maxn],b[maxn]; map<int,int>yst; int qa[maxn],f[maxn]; struct segmenttree { int l,r; int data; } tr[100005*4]; void zsbl(int now)//中序遍历 { // cout<<now<<endl; if(pot[now].lc) { zsbl(pot[now].lc); } // cout<<now; qa[++qa[0]]=a[now]; if(pot[now].rc) { zsbl(pot[now].rc); } return; } void build(int l,int r,int k) { tr[k].l=l; tr[k].r=r; if(l==r) { tr[k].data=0; return; } int mid=(l+r)/2; build(l,mid,k*2); build(mid+1,r,k*2+1); } int ask(int l,int r,int k) { if(!r)return 0; if(l<=tr[k].l&&tr[k].r<=r)return tr[k].data; if(r<tr[k].l||tr[k].r<l)return 0; return max(ask(l,r,k*2),ask(l,r,k*2+1)); } void update(int x,int k,int id) { if(tr[k].l==tr[k].r) { tr[k].data=max(tr[k].data,id); return; } int mid=(tr[k].l+tr[k].r)/2; if(x<=mid)update(x,k*2,id); else update(x,k*2+1,id); tr[k].data=max(tr[k*2].data,tr[k*2+1].data); } int main() { int n; cin>>n; for(int i = 1; i <= n; i++) { scanf("%d",&a[i]); } int x,y; for(int i = 2; i <= n; i++) { scanf("%d%d",&x,&y); pot[i].fa=x; if(!y) pot[x].lc=i; else pot[x].rc=i; } zsbl(1);//中序遍历 for(int i = 1; i <= n; i++) { qa[i]-=i; b[i]=qa[i]; } sort(b+1,b+n+1); int cntb=unique(b+1,b+n+1)-b-1; for(int i = 1; i <= cntb; i++) yst[b[i]]=i; for(int i = 1; i <= n; i++) qa[i]=yst[qa[i]]; //上头都在读入和离散化 int maxx=0; build(1,n,1); /* 数组qa是二叉搜索树中序遍历展开并离散化后得到的序列,下简称q. f的含义稍有不同,f[i]为以第q[i]个数结尾,前头都塞小于等于q[i]的数(保证序列单调递增)时最多能保留多少个数(就是能有多少个数不变。 所以,f[i]的最佳值当然是1~q[i]中最大的+1.实现查找区间内最大的并进行单点修改,当然线段树十分好用 */ for(int it = 1; it <= n; it++) { f[it]=ask(1,qa[it],1)+1; update(qa[it],1,f[it]); maxx=max(maxx,f[it]); } cout<<n-maxx; } ```