BZOJ 4358 permu

14 篇文章 0 订阅
14 篇文章 0 订阅

http://www.elijahqi.win/2017/07/07/bzoj-4358-permu/
Description
给出一个长度为n的排列P(P1,P2,…Pn),以及m个询问。每次询问某个区间[l,r]中,最长的值域
连续段长度。
Input
第一行两个整数n,m。
接下来一行n个整数,描述P。
接下来m行,每行两个整数l,r,描述一组询问。
Output
对于每组询问,输出一行一个整数,描述答案。
Sample Input
8 3
3 1 7 2 5 8 6 4
1 4
5 8
1 7
Sample Output
3
3
4
HINT
对于询问[1,4],P2,P4,P1组成最长的值域连续段[1,3];
对于询问[5,8],P8,P5,P7组成最长的值域连续段[4,6];
对于询问[1,7],P5,P7,P3,P6组成最长的值域连续段[5,8]。
1<=n,m<=50000

Source

By sumix173

首先可以想到 莫队套线段树进行修改 但 常数有点大 过不去.. 下面第二个程序 zhx巨佬写的堆建的线段树就卡过了 表示很无奈… 正解等待填坑…(updated 已经更新能过的写法
这是辣鸡蒟蒻我当时写的 莫队+权值线段树 大常数卡不过 下面会有能A的方法 菜死qwq

#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 55000
using namespace std;
int n,m,n1,ans[N],root,cnt,a[N],map[N];
inline int read(){
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
inline int max(int x,int y){
    return x>y?x:y;
}
struct node{
    int l,r,id;
}data[N];
struct node1{
    int mx,ml,mr;
    int left,right,father;
    int l,r;
}tree[4*N];
inline bool cmp(node a,node b){
    if ((a.l-1)/n1==(b.l-1)/n1) return a.r<b.r;
    return ((a.l-1)/n1<(b.l-1)/n1);
}
void build(int l,int r,int &x){
    x=++cnt;
    tree[x].l=l;tree[x].r=r;
    if (l==r){
        map[l]=x;
        tree[x].mx=tree[x].ml=tree[x].mr=0;return;
    }
    int mid=(l+r)>>1;
    build(l,mid,tree[x].left);build(mid+1,r,tree[x].right);
    int l1=tree[x].left,r1=tree[x].right;
    tree[l1].father=x;tree[r1].father=x;
    //tree[x].mx=tree[l1].mx+tree[r1].mx;
}
void print(int x){
    if (tree[x].left) print(tree[x].left);
    printf("%d %d %d %d\n",tree[x].l,tree[x].r,x,tree[x].father);
    if (tree[x].right) print(tree[x].right);
}
inline void update(int x){
    int l=tree[x].left,r=tree[x].right;
    if ((tree[l].r-tree[l].l+1)==tree[l].mx) tree[x].ml=tree[l].r-tree[l].l+1+tree[r].ml;else tree[x].ml=tree[l].ml;
    if ((tree[r].r-tree[r].l+1)==tree[r].mx) tree[x].mr=tree[r].r-tree[r].l+1+tree[l].mr;else tree[x].mr=tree[r].mr;
    tree[x].mx=max(tree[l].mx,tree[r].mx);
    tree[x].mx=max(tree[x].mx,max(tree[l].mr+tree[r].ml,tree[x].mr));
}
void insert1(int color,int f,int x){
    int l1=tree[x].l,r1=tree[x].r;
    if (l1==r1&&r1==color){
        tree[x].mx+=f;tree[x].ml=tree[x].mr=tree[x].mx;return;
    }
    int mid=(l1+r1)>>1;
    if (color<=mid) insert1(color,f,tree[x].left);else insert1(color,f,tree[x].right);
    update(x);
}
inline void update1(int fa){
    if (fa==-1) return;
    update(fa);
    update1(tree[fa].father);
}
inline void insert2(int color,int f){
    tree[map[color]].mx+=f;
    tree[map[color]].ml=tree[map[color]].mr=tree[map[color]].mx;
    update1(tree[map[color]].father);
}
int main(){
    //freopen("4358.in","r",stdin);
    //freopen("4358.out","w",stdout);
    n=read();m=read();
    for (int i=1;i<=n;++i) a[i]=read();
    for (int i=1;i<=m;++i) data[i].l=read(),data[i].r=read(),data[i].id=i;
    n1=sqrt(n);
    sort(data+1,data+1+m,cmp);
    build(1,n,root);
    tree[root].father=-1;
    //for (int i=1;i<=m;++i) printf("%d %d\n",data[i].l,data[i].r);
//  print(root);
//  printf("%d %d\n",tree[root].l,tree[root].r);
//  printf("%d %d",tree[2].l,tree[2].r);
/// printf("%d",root);
    int cl=1,cr=0;
    for (int i=1;i<=m;++i){
        int l=data[i].l,r=data[i].r,id=data[i].id;
        if (l>r){ans[id]=0;continue;}
        while (cl<l) insert2(a[cl++],-1);
        while (cl>l) insert2(a[--cl],1);
        while (cr<r) insert2(a[++cr],1);
        while (cr>r) insert2(a[cr--],-1);
        ans[id]=tree[root].mx;
    //  print(root);
    //  printf("asdfasdf\n");
    }
    for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
    return 0;
}

zhx巨佬的堆建线段树(可以卡过的

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 50005
int a[N],n,m,block,ANS[N],pos[N];
using namespace std;
struct segtree{
    int len,sl,sr,s;
}tree[N<<2];
struct query{
    int l,r,id,block;
}q[N];
inline int max(int x,int y){return x>y?x:y;}
inline int read(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline bool cmp(query x,query y){
    return x.block<y.block||x.block==y.block&&((x.r<y.r)^(x.block&1));
}
inline void pushup(int p){
    tree[p].sl=tree[p<<1].sl+(tree[p<<1].sl==tree[p<<1].len)*tree[p<<1|1].sl;
    tree[p].sr=tree[p<<1|1].sr+(tree[p<<1|1].sr==tree[p<<1|1].len)*tree[p<<1].sr;
    tree[p].s=max(tree[p<<1].s,tree[p<<1|1].s);
    tree[p].s=max(tree[p].s,tree[p<<1].sr+tree[p<<1|1].sl);
}
void build(int p,int l,int r){
    tree[p].len=r-l+1;
    if(tree[p].len==1){
        tree[p].sl=tree[p].sr=tree[p].s=0;
        pos[l]=p;return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
}
inline void change(int x){
    int p=pos[x];
    tree[p].sl=tree[p].sr=(tree[p].s^=1);
    while(p!=1) pushup(p=p>>1);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();//block=sqrt(n);
    for(int i=1;i<=n;++i) a[i]=read();
    build(1,1,n);
    for(int i=1;i<=m;++i){
        q[i].l=read();q[i].r=read();q[i].id=i;
        q[i].block=sqrt(q[i].l);
    }
    sort(q+1,q+m+1,cmp);
    int l=1,r=0;
    for(int i=1;i<=m;++i){
        if(q[i].l>q[i].r){
             ANS[q[i].id]=0;continue;
        }
        for(;l<q[i].l;++l) change(a[l]);
        for(;l>q[i].l;--l) change(a[l-1]);
        for(;r>q[i].r;--r) change(a[r]);
        for(;r<q[i].r;++r) change(a[r+1]);
        ANS[q[i].id]=tree[1].s;
    }
    for(int i=1;i<=m;++i) printf("%d\n",ANS[i]);
    return 0;
}

正解用了并查集 并且保证了莫队的单调性 并查集怕删除于是乎解决删除操作就成了很好的选择

end数组表示每一块最后的位置 bl表示每个点所属的块

1、若p块所在与m块相同 ++p直到p出了该块

2、清空fa(并查集)

cur当前=i所在块中的最后一位的位置

3、循环i~p将当前在块内的询问操作处理

使用并查集将属于块外的查询的值进行储存

4、暴力计算在块内对答案的影响(循环从左端点到右端点或者是该块的末端)

并查集的实现注意细节

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 55000
using namespace std;
int n,m,n1,a[N],bl[N],end[N],tmp,fa[N],fa2[N],s[N],ans[N];
inline int read(){
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
struct node{
    int l,r,id;
}data[N];
inline bool cmp(node a,node b){
    if ((a.l-1)/n1==(b.l-1)/n1) return a.r<b.r;
    return a.l<b.l;
}
inline int min(int x,int y){
    return x<y?x:y;
}
inline int max(int x,int y){
    return x>y?x:y;
}
inline int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline int find2(int x){
    return fa2[x]==x?x:fa2[x]=find2(fa2[x]);
} 
void add(int u){
    int v;fa[u]=u;s[u]=1;
    if (v=find(u-1)) fa[v]=u,s[u]+=s[v];
    if (v=find(u+1)) fa[v]=u,s[u]+=s[v];
    tmp=max(tmp,s[u]);
}
int mx[N],mn[N],S[N],top;
void add2(int u,int &reci){
    int v;fa2[u]=u;mx[u]=mn[u]=u;S[++top]=u;
    if (v=find2(u-1)) mn[u]=mn[v];
    else if (v=find(u-1)) mn[u]-=s[v];
    if (v=find2(u+1)) mx[u]=mx[v];
    else if (v=find(u+1)) mx[u]+=s[v];
    reci=max(reci,mx[u]-mn[u]+1);
    S[++top]=mx[u];fa2[mx[u]]=u;
    S[++top]=mn[u];fa2[mn[u]]=u;

}
int main(){
    //freopen("4358.in","r",stdin);
    //freopen("4358.out","w",stdout);
    n=read();m=read();
    n1=sqrt(n);
    for (int i=1;i<=n;++i) a[i]=read(),bl[i]=(i-1)/n1+1,end[bl[i]]=i;//end表示该块的最后一位在何位置 
    for (int i=1;i<=m;++i) data[i].l=read(),data[i].r=read(),data[i].id=i;
    sort(data+1,data+m+1,cmp); 
    for (int i=1;i<=m;++i){
        int p=i,cur;tmp=0;
        while (bl[data[p].l]==bl[data[i].l]&&p<=m) ++p; 
        memset(fa,0,sizeof(fa));
        cur=end[bl[data[i].l]];
        for (int j=i;j<p;++j){
            while (cur<data[j].r) add(a[++cur]);
            ans[data[j].id]=tmp;
            for (int z=data[j].l;z<=min(data[j].r,end[bl[data[j].l]]);++z) add2(a[z],ans[data[j].id]);
            while (top) fa2[S[top--]]=0;
        }
        i=p-1;
    }
    for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
    return 0;
}

kd-tree的“暴力”做法orz %%icefox巨佬 思维神速

kd-tree怎么搞 建议先参考一下这题

http://blog.csdn.net/elijahqi/article/details/79050999

根据这题现有的思路 因为我给出的是一个排列 那么可以知道这个对应数值的位置在原来的串中的哪个位置 既然是连续的 那么我们就从权值从小到大去kd-tree上去跑 每次如果这个完全覆盖了kd-tree上的那段区间就+1 然后如果没有覆盖就完整的打上0的标记 最后针对每个节点 相当于求一个历史最大值 那么就用到了上文提到的链接中的那个做法了

#include<cstdio>
#include<algorithm>
#define N 55000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x;
}
int D,root; 
struct node1{
    int d[2],id;
    int& operator[](int x){return d[x];}
    friend bool operator<(node1 a,node1 b){return a[D]<b[D];}
}point[N];
struct node{
    node1 x;int left,right,min[2],max[2],cover,add,max1,last_cover,last_add,last_max;
}tree[N];
inline void update(int x){
    int l=tree[x].left,r=tree[x].right;
    for (int i=0;i<2;++i) tree[x].min[i]=min(tree[x].min[i],min(tree[l].min[i],tree[r].min[i]));
    for (int i=0;i<2;++i) tree[x].max[i]=max(tree[x].max[i],max(tree[l].max[i],tree[r].max[i]));
}
inline void build(int &x,int l,int r,int dim){
    int mid=l+r>>1;x=mid;D=dim;nth_element(point+l,point+mid,point+r+1);
    tree[x].x=point[mid];for (int i=0;i<2;++i) tree[x].min[i]=tree[x].max[i]=point[mid][i];
    tree[x].cover=tree[x].last_cover=-inf;tree[x].last_max=tree[x].max1=tree[x].add=tree[x].last_add=0;
    if (l<mid) build(tree[x].left,l,mid-1,dim^1);
    if (r>mid) build(tree[x].right,mid+1,r,dim^1);update(x);
}
inline void change1(int x,int v){
    tree[x].last_max=max(tree[x].last_max,tree[x].max1=v);tree[x].add=0;
    tree[x].last_cover=max(tree[x].last_cover,tree[x].cover=v);
}
inline void add(int x,int v){
    tree[x].last_max=max(tree[x].last_max,tree[x].max1+=v);
    if (tree[x].cover>-inf) tree[x].last_cover=max(tree[x].last_cover,tree[x].cover+=v);
    else tree[x].last_add=max(tree[x].last_add,tree[x].add+=v);
}
inline void ladd(int x,int v){
    tree[x].last_max=max(tree[x].last_max,tree[x].max1+v);
    if (tree[x].cover>-inf) tree[x].last_cover=max(tree[x].last_cover,tree[x].cover+v);
    else tree[x].last_add=max(tree[x].last_add,tree[x].add+v);
}
inline void lc(int x,int v){
    tree[x].last_max=max(tree[x].last_max,v);tree[x].last_cover=max(tree[x].last_cover,v);
}
inline void pushdown(int x){
    if (tree[x].last_add)
        ladd(tree[x].left,tree[x].last_add),ladd(tree[x].right,tree[x].last_add),tree[x].last_add=0;
    if (tree[x].last_cover>-inf)
        lc(tree[x].left,tree[x].last_cover),lc(tree[x].right,tree[x].last_cover),tree[x].last_cover=-inf;
    if (tree[x].add)
        add(tree[x].left,tree[x].add),add(tree[x].right,tree[x].add),tree[x].add=0;
    if (tree[x].cover>-inf)
        change1(tree[x].left,tree[x].cover),change1(tree[x].right,tree[x].cover),tree[x].cover=-inf;
}
inline void change(int x,int p){
    if (p<tree[x].min[0]||p>tree[x].max[1]) {change1(x,0);return;}
    if (p>=tree[x].max[0]&&p<=tree[x].min[1]) {add(x,1);return;}pushdown(x);
    if (tree[x].x[0]<=p&&tree[x].x[1]>=p) {
        tree[x].last_max=max(tree[x].last_max,tree[x].max1+=1);
    }else tree[x].max1=0;
    if (tree[x].left) change(tree[x].left,p);if (tree[x].right) change(tree[x].right,p);
}
inline void dfs(int x){
    pushdown(x);
    if (tree[x].left) dfs(tree[x].left);
    if (tree[x].right) dfs(tree[x].right);
}
int ans[N],a[N],n,m;
inline void print(int x,int dim){
    if (tree[x].left) print(tree[x].left,dim^1);
    printf("%d %d %d %d %d\n",tree[x].min[0],tree[x].max[0],tree[x].min[1],tree[x].max[1],dim);
    if (tree[x].right) print(tree[x].right,dim^1);
}
int main(){
    freopen("bzoj4358.in","r",stdin);
    n=read();m=read();int t=0;for (int i=0;i<2;++i) tree[0].min[i]=inf;
    for (int i=1;i<=n;++i) t=read(),a[t]=i;
    //for (int i=1;i<=n;++i) printf("%d ",a[i]);printf("\n");
    for (int i=1;i<=m;++i)
        for (int j=0;j<2;++j) point[i][j]=read(),point[i].id=i;
    build(root,1,m,0);//print(root,0);
    for (int i=1;i<=n;++i) change(root,a[i]);
    dfs(root);for (int i=1;i<=m;++i) ans[tree[i].x.id]=tree[i].last_max;
    for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值