【20180706 Duliu Round 2】总结 【数据结构】*

T1 序列

给一个长为 n 的序列,m 次询问区间中差最小的两个数的差。
Input
第一行两个整数 n,m。
第二行 n 个整数 a1,…,an,表示序列。
接下来 m 行,每行两个整数 l,r,表示一次询问。
Output
对于每个询问,输出一行答案。
Sample Input
3 2
1 2 1
1 2
1 3
Sample Output
1
0
Constraints
子任务 1(30%):n,m≤5000。
子任务 2(30%):数据随机。
子任务 3(40%):无特殊限制。
对于全部数据
2 n 10 5 , 1 m 3 10 5 , 0 a i 10 9 , 1 l < r n


毒瘤,原题CF 765F

首先因为绝对值符号不好搞,我们就把绝对值符号去掉,我们先考虑当 i < j a i > a j 的情况,另外一种情况直接把序列翻转一遍就可以求出

现在我们考虑对于每一个节点i当做区间右节点的时候,我们考虑维护所有可能左节点的答案。我们寻找一个j满足同时满足 j < i a j >= a i 且j最大,这样很容易发现对于任意的 k > j 都不可能更新答案,所以我们可以用 a j a i 的值更新 1.. j 的答案。然后再找到最大的 j < j 使得 a i a j < a j ,显然j′+1往后的答案不会被修改,可以用 a j a i 的值更新 1.. j 的答案。然后我们再用权值线段树维护一下每个数最后出现的位置就好了

#include<bits/stdc++.h>
using namespace std;
#define pi pair<int,int>
#define mp(x,y) make_pair(x,y)
#define INF 0x3f3f3f3f
#define N 400010
int n,m,cnt,pre[N],a[N],b[N];
int ql[N],qr[N],ans[N];
vector<pi > g[N];
struct Segment_tree{
    #define LD (t<<1)
    #define RD ((t<<1)|1)
    int val[N],tag[N];
    void clear(){
        memset(tag,0x3f,sizeof(tag));
        memset(val,0xff,sizeof(val));
    }
    int findmax(int t,int l,int r,int ql,int qr){
        if(ql<=pre[l]&&pre[r]<=qr)return val[t];
        int mid=(l+r)>>1,res=-1;
        if(ql<=pre[mid])res=max(res,findmax(LD,l,mid,ql,qr));
        if(pre[mid+1]<=qr)res=max(res,findmax(RD,mid+1,r,ql,qr));
        return res;
    }
    void Insert(int t,int l,int r,int pos,int vl){
        if(l>r)return;
        if(l==r){val[t]=vl;return;}//记录每个数的最后出现位置
        int mid=(l+r)>>1;
        if(pos<=mid)Insert(LD,l,mid,pos,vl);
        else Insert(RD,mid+1,r,pos,vl);
        val[t]=max(val[LD],val[RD]);
    }
    void modify(int t,int l,int r,int ql,int qr,int vl){
        if(l>r)return;
        if(ql<=l&&r<=qr){tag[t]=min(tag[t],vl);return;}
        int mid=(l+r)>>1;
        if(ql<=mid)modify(LD,l,mid,ql,qr,vl);
        if(qr>mid)modify(RD,mid+1,r,ql,qr,vl);
    }
    int query(int t,int l,int r,int pos){
        if(l==r)return tag[t];//****** t和pos不要弄混
        int mid=(l+r)>>1;
        if(pos<=mid){
            tag[LD]=min(tag[LD],tag[t]);
            return query(LD,l,mid,pos);
        }else{
            tag[RD]=min(tag[RD],tag[t]);
            return query(RD,mid+1,r,pos);
        }
    }
}t;
void init(){memset(ans,0x3f,sizeof(ans));}
void solve(){
    t.clear();
    for(int i=1;i<=n;i++){
        int x=a[i],y=INF*2-x;
        int p=t.findmax(1,1,cnt,x,(x+y)>>1);
        while(x<y&&p!=-1){
            t.modify(1,1,n,1,p,a[p]-a[i]);
            y=a[p];
            if(x>=y)break;//******* 不加这个会GG掉
            p=t.findmax(1,1,cnt,x,(x+y)>>1);
        }
        for(int j=0;j<g[i].size();j++)
            ans[g[i][j].second]=min(ans[g[i][j].second],t.query(1,1,n,g[i][j].first));
        t.Insert(1,1,cnt,b[i],i);
    }
}
int main(){
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),pre[i]=a[i];
    sort(pre+1,pre+n+1);
    cnt=unique(pre+1,pre+n+1)-pre-1;
    for(int i=1;i<=n;i++)b[i]=lower_bound(pre+1,pre+cnt+1,a[i])-pre;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&ql[i],&qr[i]);
        g[qr[i]].push_back(mp(ql[i],i));
    }
    solve();
    for(int i=1;i<=n/2;i++)swap(a[i],a[n-i+1]),swap(b[i],b[n-i+1]);
    for(int i=1;i<=n;i++)g[i].clear();//******* 注意g需要清空
    for(int i=1;i<=m;i++)
        g[n-ql[i]+1].push_back(mp(n-qr[i]+1,i));
    solve();
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

T2 图

给一个 n 个点,m 条边的无向图,有点权和边权。另外给出 q 组询问,每次询问点 x 出发只经过边权不超过 v 的路径所能到达的点中第 k 大的点权。
Input
第一行三个整数 n,m,q。
第二行 n 个整数 a1,…,an,表示点权。
接下来 m 行,每行三个整数 x,y,w,表示 x 和 y 之间有一条边权为 w 的边。
接下来 q 行,每行三个整数 x,v,k,表示一次询问。
Output
对于每个询问,输出一行答案,无解输出 −1。
Sample Input
3 1 3
1 2 3
1 2 2
1 1 2
1 2 2
3 1 1
Sample Output
-1
1
3
Constraints
子任务 1(40%):n,m,q≤2000。
子任务 2(60%):无特殊限制。
对于全部数据
1 n 10 5 , 1 m , q 5 10 5 , 1 x , y , k n , 0 a i , w , v 10 9

考虑离线询问,对询问和边一起排序,保证任意询问时在图中的边权都小于等于当前查询值,然后并查集维护一下线段树合并就好

#include<bits/stdc++.h>
using namespace std;
#define N 1000010
#define M 5000010
inline int read(){
    int data=0,w=1; char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(isdigit(ch))data=(data<<1)+(data<<3)+(ch-'0'),ch=getchar();
    return data*w;
}
struct Edge{int u,v,w,id;bool q;}E[N];
int n,m,q;
int rt[N],ls[M],rs[M],siz[M],ans[N],tot=0;
int a[N],b[N],pre[N],fa[N];
bool cmp(Edge a,Edge b){
    if(a.w==b.w)return a.q<b.q;
    return a.w<b.w;
}
void init(){for(int i=1;i<=n;i++)fa[i]=i;}
int findfa(int x){
    if(fa[x]==x)return x;
    return fa[x]=findfa(fa[x]);
}
void pushup(int t){siz[t]=siz[ls[t]]+siz[rs[t]];}
void insert(int &t,int l,int r,int pos){
    if(!t)t=++tot;
    if(l==r){siz[t]=1;return;}
    int mid=(l+r)>>1;
    if(pos<=mid)insert(ls[t],l,mid,pos);
    else insert(rs[t],mid+1,r,pos);
    pushup(t);
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(!ls[x]&&!rs[x]){siz[x]+=siz[y];return x;}
    ls[x]=merge(ls[x],ls[y]);
    rs[x]=merge(rs[x],rs[y]);
    pushup(x);
    return x;
}
int query(int x,int l,int r,int k){
    if(l==r)return l;
    int mid=(l+r)>>1;
    if(siz[ls[x]]>=k)return query(ls[x],l,mid,k);
    return query(rs[x],mid+1,r,k-siz[ls[x]]);
}
int main(){
    n=read();m=read();q=read();
    init();
    for(int i=1;i<=n;i++)a[i]=b[i]=read();
    sort(b+1,b+n+1);
    int cnt=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++){
        int tmp=a[i];
        a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
        pre[a[i]]=tmp;
    }
    for(int i=1;i<=n;i++)insert(rt[i],1,n,a[i]);
    for(int i=1;i<=m;i++){
        E[i].u=read();
        E[i].v=read();
        E[i].w=read();
        E[i].q=0;
    }
    for(int i=1+m;i<=m+q;i++){
        E[i].u=read();
        E[i].w=read();
        E[i].v=read();
        E[i].q=1;
        E[i].id=i-m;
    } 
    sort(E+1,E+m+q+1,cmp);
    int tmp=0;
    for(int i=1;i<=m+q;i++){
        if(!E[i].q){
            if(tmp==n-1)continue;
            int u=findfa(E[i].u);
            int v=findfa(E[i].v);
            if(u!=v){
                fa[u]=v,++tmp;
                rt[v]=merge(rt[u],rt[v]);
            }
        }else{
            int x=findfa(E[i].u);
            if(siz[rt[x]]<E[i].v){ans[E[i].id]=-1;continue;}
            int res=query(rt[x],1,n,siz[rt[x]]-E[i].v+1);
            ans[E[i].id]=pre[res];
        }
    }
    for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
    return 0;
}

T3 树

有 n 个不连通的有点权的点和 m 个操作。共有三种操作:
1 x y 表示如果 x 和 y 不连通,则在 x 和 y 之间连一条无向边并输出 1,否则输出 0。
2 x v 表示把 x 的点权修改为 v。
3 x y 表示查询 x 到 y 路径上的点权和。若 x 和 y 不连通,输出 −1。
Input
第一行两个整数 n,m。
第二行 n 个整数 a1,…,an,表示初始点权。
接下来 m 行,每行三个整数,表示一次操作。
Output
对于每个 1,3 操作,输出一行答案。
Sample Input
2 7
1 2
3 1 1
3 1 2
1 1 2
1 1 2
3 1 2
2 1 3
3 1 2
Sample Output
1
-1
1
0
3
5
Constraints
子任务 1(40%):n,m≤5000。
子任务 2(60%):无特殊限制。
对于全部数据
1 n 105 , 1 m 3 105 , 0 a i , v 109 , 1 x , y n

LCT板子做掉

#include<bits/stdc++.h>
using namespace std;
#define N 100010 
#define LL long long
inline int read(){
    int ans=0,w=1;char c=getchar();
    while(!isdigit(c)&&c!='-')c=getchar();
    if(c=='-')w=-1,c=getchar();
    while(isdigit(c))ans=ans*10+c-'0',c=getchar();
    return ans*w;
}
int n,m;
int fa[N],son[N][2],st[N];
LL val[N],sum[N];
bool rev[N]={0};
inline bool isroot(int x){
    return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
}
void pushup(int x){
    sum[x]=sum[son[x][1]]+sum[son[x][0]]+val[x];
}
void pushdown(int x){
    if(rev[x]){
        rev[x]^=1;
        rev[son[x][0]]^=1;
        rev[son[x][1]]^=1;
        swap(son[x][0],son[x][1]);
    } 
}
inline bool Son(int t){return son[fa[t]][1]==t;}
inline void rotate(int x){
    int f=fa[x],g=fa[f],a=Son(x),b=a^1;
    if(!isroot(f)){
        if(Son(f))son[g][1]=x;
        else son[g][0]=x;
    }
    fa[x]=g;fa[f]=x;fa[son[x][b]]=f;
    son[f][a]=son[x][b];son[x][b]=f;
    pushup(f);
    pushup(x);
}
void splay(int x){
    int top=0;st[++top]=x;
    for(int i=x;!isroot(i);i=fa[i])st[++top]=fa[i];
    for(int i=top;i;i--)pushdown(st[i]);
    while(!isroot(x)){
        int f=fa[x];
        if(!isroot(f)){
            if(Son(x)^Son(f))rotate(x);
            else rotate(f);
        }
        rotate(x);
    }
    pushup(x);
}
void access(int x){
    int t=0;
    while(x){
        splay(x);
        son[x][1]=t;
        t=x;x=fa[x];
    } 
}
void reverse(int x){
    access(x);
    splay(x);
    rev[x]^=1;
}
void link(int x,int y){
    reverse(x);
    fa[x]=y;
    splay(x);
}
void cut(int x,int y){
    reverse(x);
    access(y);
    splay(y);
    son[y][0]=fa[x]=0;
}
int find(int x){
    access(x);
    splay(x);
    int f=x;
    while(son[f][0])f=son[f][0];
    return f;
}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
    for(int i=1;i<=m;i++){
        int op=read(),x=read(),y=read();
        if(op==1){
            if(find(x)==find(y))printf("0\n");
            else{
                link(x,y);
                printf("1\n");
            }
        }
        if(op==2){
            splay(x);
            val[x]=y;
            pushup(x);
        }
        if(op==3){
            if(find(x)!=find(y))printf("-1\n");
            else{
                reverse(x);
                access(y);
                splay(y);
                printf("%lld\n",sum[y]);
            }
        }
    }
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/dream_maker_yk/article/details/80947241