zoukankan      html  css  js  c++  java
  • [noip 2013]火柴排队(树状数组/线段树 求逆序对) 2017-05-24 09:36 33人阅读 评论(0) 收藏

    描述

    涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: ∑(ai-bi)^2

    其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。

    每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
    格式

    输入格式

    共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
    第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
    第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
    输出格式

    输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果
    样例1

    样例输入1

    4
    2 3 1 4
    3 2 1 4

    样例输出1

    1

    样例2

    样例输入2

    4
    1 3 4 2
    1 7 2 4
    样例输出2

    2
    限制

    每个测试点1s。
    提示

    样例1说明

    最小距离是 0,最少需要交换 1 次,比如:交换第 1 列的前 2 根火柴或者交换第 2 列的前 2 根火柴。

    样例2说明

    最小距离是 10,最少需要交换 2 次,比如:交换第 1 列的中间 2 根火柴的位置,再交换第 2 列中后 2 根火柴的位置。

    数据范围

    对于 10%的数据, 1 ≤ n ≤ 10;
    对于 30%的数据,1 ≤ n ≤ 100;
    对于 60%的数据,1 ≤ n ≤ 1,000;
    对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ 2^31 − 1。
    来源

    NOIP 2013 提高组 Day 1

    思路:将两个数组排序
    然后a中第一小的对应b中第一小的
    a中第二小的对应b中第二小的….
    a中最大对应b中最大

    用一个d数组记录 d[a[i].p]=b[i].p;
    那么这个数组中逆序对的对数就是题目的答案 因为要交换 个个对应
    用树状数组求逆序对的模板就好

    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    #define N 100010
    #define MOD 99999997
    using namespace std;
    int n;int d[N],c[N];
    struct node{
        int v,p;
    }a[N],b[N];
    bool cmp(node xx,node yy){
        return xx.v<yy.v;
    }
    int lowbit(int x){
        return x&-x;
    }
    void update(int x){
        while(x<=n){
            c[x]++;
            x+=lowbit(x);
        }
    }
    int getsum(int x){
        int sum=0;
        while(x>0){
            sum+=c[x];
            x-=lowbit(x);
        }
        return sum;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i].v);
            a[i].p=i;
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&b[i]);
            b[i].p=i;
        }
        sort(a+1,a+1+n,cmp);
        sort(b+1,b+1+n,cmp);
        for(int i=1;i<=n;i++)
            d[a[i].p]=b[i].p;
        int ans=0;
        for(int i=1;i<=n;i++){
            update(d[i]);
            ans=(ans+i-getsum(d[i]))%MOD;
        }
        cout<<ans;
        return 0;
    }
    

    线段树

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define N 100010
    #define MOD 99999997
    using namespace std;
    int w[N];
    int d[N];
    struct node{
        int left;
        int right;
        int sum;
    }a[4*N];
    void build(int l,int r,int p){//1为根节点建立线段树 建立一个空的树 
        a[p].left=l;a[p].right=r;
        if(l==r) {  a[p].sum=0;return ;}
        if(l<r){
            build(l,(l+r)/2,2*p);
            build((l+r)/2+1,r,2*p+1);
            a[p].sum=a[2*p].sum+a[2*p+1].sum;
        }
    }
    void update(int l,int r,int p,int d){//向以1为根节点的区间[l,r]插入数字1
        if(a[p].left==l&&a[p].right==r){
            a[p].sum+=d;return ;
        }
        int mid=(a[p].left+a[p].right)/2;
        if(r<=mid) update(l,r,2*p,d);
        else if(l>mid) update(l,r,2*p+1,d);
        else{
            update(l,mid,2*p,d);update(mid+1,r,2*p+1,d);
        }
        a[p].sum=a[2*p].sum+a[2*p+1].sum;
    }
    int query(int l,int r,int p){//查询以1为根节点,区间[l,r]的和
        if(a[p].left==l&&a[p].right==r)
            return a[p].sum;
        int mid=(a[p].left+a[p].right)/2;
        if(r<=mid) return query(l,r,2*p);
        else if(l>mid) return query(l,r,2*p+1);
        else return query(l,mid,2*p)+query(mid+1,r,2*p+1);
    }
    struct data{
        int v;
        int p;
    }h[N],b[N];
    bool cmp(data xx,data yy){
        return xx.v<yy.v;
    }
    int main(){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&h[i].v);
            h[i].p=i;
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&b[i]);
            b[i].p=i;
        }
        sort(h+1,h+1+n,cmp);
        sort(b+1,b+1+n,cmp);
        for(int i=1;i<=n;i++)
            d[h[i].p]=b[i].p;
        int ans=0;
        build(1,n,1);
        for(int i=1;i<=n;i++){
            update(d[i],d[i],1,1);  //把线段树[d[i],d[i]]区间的值插入为1
            ans=(ans+i-query(1,d[i],1))%MOD;//求逆序对加取模操作 
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    Ajax笔记(三)
    Ajax笔记(二)
    org.apache.commons.lang.StringUtils中常用的方法
    数位dp poj1850
    二分图 最小点覆盖 poj 3041
    poj 1789 prime
    c++三种进制格式
    c++面向行的输入getline()和get()
    最小生成树 prime算法 UVALive
    最短路 poj1125
  • 原文地址:https://www.cnblogs.com/xljxlj/p/7183647.html
Copyright © 2011-2022 走看看