【TJOI2016&&HEOI2016】序列

48 篇文章 0 订阅
18 篇文章 0 订阅

Description

这里写图片描述

Solution

比赛时乱水

开始打题后最先想了这一题:第一题太水,第二题太机智,没办法啊……TAT
然后打了个LIS(用二分n log n进行DP,得出最长不下降子序列)进行暴搜,后来想到可以预处理出所有改变值得最大值和最小值,然后就变成了一个二维的最长不下降子序列,不知道为什么,第一意识就打了个贪心:转移以最大值为第一关键字,a值为第二关键字。
然后竟然100分水过,我还真以为是对的,模模糊糊的讲完题后,被samjia的数据卡掉了。

树套树

作为第一次打树状数组套线段树,也是挺有意义的。
用mx[i]表示在i这个位置变化的最大值,mi[i]表示在i这个位置变化的最小值。(包含初始值)
那么先从DP入手。

f[i]=f[j]+1(ji,mx[j]a[i],a[j]mi[i])

一共要满足三个条件。
条件1,从左到右做很容易满足。
条件2和3有些棘手:我们观察后面的关系式可以发现只与i和j有关,那么我们试着把关系式放到二维平面上表示。那么很显然,在上面的式子都是≤符号是,只需要找所有的 mx[j]a[i],a[j]mi[i] 。所以每次把点(mx[i],a[i])放入二维平面,然后每次找所有在(1,1)到(a[i],mi[i])这个矩形内的点的最大值。
这个东西用树套树就可以了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=100007,da=100007;
int i,j,k,l,n,m,ans,r,mid;
int f[maxn],a[maxn],p[maxn],q[maxn],num,mx[maxn],mi[maxn],root[maxn];
bool bz,az;
struct node{
    int l,r,sum;
}t[maxn*100];
int lowbit(int x){return x&(-x);}
int find(int x,int l,int r,int y,int z){
    int i,o=0;
    if(!x)return 0;
    if(l==y&&r==z){
        return t[x].sum;
    }
    int mid=(l+r)/2;
    if(z<=mid)return find(t[x].l,l,mid,y,z);
    else if(y>mid)return find(t[x].r,mid+1,r,y,z);
    else{
        return max(find(t[x].l,l,mid,y,mid),find(t[x].r,mid+1,r,mid+1,z));
    }
}
void change(int &x,int l,int r,int y,int z){
    if(!x)x=++num;
    t[x].sum=max(t[x].sum,z);
    if(l==r)return;
    int mid=(l+r)/2;
    if(y<=mid)change(t[x].l,l,mid,y,z);
    else change(t[x].r,mid+1,r,y,z);
}
int main(){
//  freopen("fan.in","r",stdin);
    scanf("%d%d",&n,&m);
    fo(i,1,n)scanf("%d",&a[i]),mx[i]=mi[i]=a[i];
    fo(i,1,m){
        scanf("%d%d",&k,&l);mx[k]=max(l,mx[k]);mi[k]=min(l,mi[k]);
    }
    fo(i,1,n){
        int o=0;
        for(j=mi[i],p[0]=0;j;j-=lowbit(j))o=max(o,find(root[j],1,da,1,a[i]));
        f[i]=o+1;
        ans=max(ans,f[i]);
        for(j=a[i];j<=da;j+=lowbit(j))change(root[j],1,da,mx[i],f[i]);
    }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值