zoukankan      html  css  js  c++  java
  • 线段树和平方分割

    POJ 2104 K-th Number 

    题意:给出一段数列,让你求[L,R]区间内第k大的数字

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    const int  MAXN = 100010;
    const int N = MAXN*40;
    int n,m,q,tot;
    int T[MAXN],A[MAXN],t[MAXN];
    int lson[N],rson[N],sum[N];
    vector<int>V;
    int getid(int x) //离散化
    {
        return lower_bound(V.begin(),V.end(),x)-V.begin()+1;
    }
    int build(int l,int r)  //建立一棵空树
    {
        int rt = tot++;
        sum[rt] = 0;
        if(l!=r){
            int mid=(l+r)>>1;
            lson[rt] = build(l,mid);
            rson[rt] = build(mid+1,r);
        }
        return rt;
    }
    
    int update(int rt,int pos)  //把数组中的元素一次加入新的线段树中
    {
        int nrt = tot++;
        int tmp = nrt;
        sum[nrt]  = sum[rt]+1;
        int l=1,r=m;
        while(l<r) {
            int mid = (l+r)>>1;
            if(pos<=mid) {
                lson[nrt] = tot++;
                rson[nrt] = rson[rt];
                nrt = lson[nrt];
                rt = lson[rt];
                r = mid;
            }else {
                rson[nrt] = tot++;
                lson[nrt] = lson[rt];
                nrt = rson[nrt];
                rt = rson[rt];
                l=mid+1;
            }
            sum[nrt] = sum[rt]+1;
        }
        return tmp;
    }
    
    int query(int lrt,int rrt,int k)
    {
        int l=1,r=m;
        while(l<r) {
            int mid = (l+r)>>1;
            int cnt = sum[lson[rrt]] - sum[lson[lrt]];
            if(cnt>=k) {
                r = mid;
                lrt = lson[lrt];
                rrt = lson[rrt];
            } else {
                l = mid+1;
                k-=cnt;
                lrt = rson[lrt];
                rrt = rson[rrt];
            }
        }
        return l;
    }
    int main()
    {//freopen("in.txt","r",stdin);
        scanf("%d%d",&n,&q);tot=0;
        for(int i=1;i<=n;i++) {
            scanf("%d",&A[i]);
            V.push_back(A[i]);
        }
        sort(V.begin(),V.end());
        V.erase(unique(V.begin(),V.end()),V.end());
        m=V.size();
        T[0] = build(1,m);
        for(int i=1;i<=n;i++) {
            T[i] = update(T[i-1],getid(A[i]));
        }
        while(q--) {
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            printf("%d
    ",V[query(T[x-1],T[y],k)-1]);
        }
        return 0;
    }
    

      

    POJ 2528 Mayor's posters

    题意:贴海报,海报可以覆盖,会给出你每张海报的长度及起始位置,然后问你最后还能看到几张海报。

    #include<math.h>
    #include<string.h>
    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    const int maxn=10005;
    int n;
    int vis[maxn<<3],sum[maxn<<4];
    int li[maxn*2],ri[maxn*2],lsh[maxn<<2];
    void pushdown(int rt)
    {
        sum[rt<<1]=sum[rt];
        sum[rt<<1|1]=sum[rt];
        sum[rt]=-1;
    }
    void update(int L,int R,int C,int l,int r,int rt)
    {
        if(L<=l&&r<=R)
        {
            sum[rt]=C;
            return ;
        }
        if(sum[rt]!=-1)
            pushdown(rt);
        int m=(l+r)>>1;
        if(m>=R) update(L,R,C,l,m,rt<<1);
        else if(L>m) update(L,R,C,m+1,r,rt<<1|1);
        else update(L,m,C,l,m,rt<<1),update(m+1,R,C,m+1,r,rt<<1|1);
    }
    int ans;
    void query(int l,int r,int rt)
    {
         if(!vis[sum[rt]]&&sum[rt]!=-1)
        {
            ans++;
            vis[sum[rt]]=1;
            return ;
        }
        if(l==r)
        {
            return ;
        }
        if(sum[rt]!=-1)
            pushdown(rt);
        int m=(l+r)>>1;
        query(l,m,rt<<1);
        query(m+1,r,rt<<1|1);
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&n);
            memset(sum,-1,sizeof(sum));
            memset(vis,0,sizeof(vis));
            int tot=0;
            for(int i=0;i<n;i++)
            {
                scanf("%d%d",&li[i],&ri[i]);
                lsh[tot++]=li[i];
                lsh[tot++]=ri[i];
            }
            sort(lsh,lsh+tot);
            int mm=unique(lsh,lsh+tot)-lsh;
            int tt=mm;
            for(int i=1;i<tt;i++)
            {
                if(lsh[i]-lsh[i-1]>1)
                    lsh[mm++]=lsh[i-1]+1;
            }
            sort(lsh,lsh+mm);
            for(int i=0;i<n;i++)
            {
                int x=lower_bound(lsh,lsh+mm,li[i])-lsh;
                int y=lower_bound(lsh,lsh+mm,ri[i])-lsh;
                update(x,y,i,0,mm-1,1);
            }
            ans=0;
            query(0,mm-1,1);
            printf("%d
    ",ans);
        }
    }
    

      

    POJ 3264 Balanced Lineup

    题意:找到一段数字里最大值和最小值的差

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int MAX_N = 5e4 + 5;
    const int INF = 0x3f3f3f3f;
    typedef pair<int, int> P;
    
    P dat[4 * MAX_N];//存储线段树的全局数组
    int n;
    //初始化
    void init(int N) {
        n = 1;
        while (n < N) n <<= 1;//简单起见,把元素个数扩大到2的幂
        for (int i = 0; i < 2 * n - 1; ++i) {
            dat[i].first = INF;//存储区间最小值
            dat[i].second = -INF;//存储区间最大值
        }
    }
    //把第k个值更新为x
    void update(int k, int x) {
        k += n - 1;
        dat[k] = P(x, x);
        while (k > 0) {//向上更新
            k = (k - 1) / 2;
            dat[k].first = min(dat[2 * k + 1].first, dat[2 * k + 2].first);
            dat[k].second = max(dat[2 * k + 1].second, dat[2 * k + 2].second);
        }
    }
    //查询
    P query(int a, int b, int k, int l, int r) {//k是节点编号
        if (a <= l && r <= b) return dat[k];
        if (a > r || b < l) return P(INF, -INF);
        P vl = query(a, b, 2 * k + 1, l, (l + r) / 2);
        P vr = query(a, b, 2 * k + 2, (l + r) / 2 + 1, r);
        return P(min(vl.first, vr.first), max(vl.second, vr.second));
    }
    int main() {
        int N, Q;
        scanf("%d%d", &N, &Q);
        init(N);
        for (int i = 0; i < N; ++i) {
            int x;
            scanf("%d", &x);
            update(i, x);
        }
        for (int i = 0; i < Q; ++i) {
            int a, b;
            scanf("%d%d", &a, &b);
            P p = query(a - 1, b - 1, 0, 0, n - 1);
            printf("%d
    ", p.second - p.first);
        }
        return 0;
    }
    

     还有一种基于稀疏表的 RMQ 的方法

    预处理:
    预处理是采用dp的思想,用f[i][j]表示区间[i,i+2^j-1]中的最大值
    (即从i开始,长度为2^j的闭区间)。开始时,f[i][0] 就是区间[i][i]的值,
    即f[i][0] = num[i],好了,初始值找到了,下面是状态转移方程:
    f[i][j] = max (f[i][j-1],f[i+2^(j-1)][j-1])。即把[i,i+2^j-1]区间分为
    [i,i+2^(j-1)-1]和[j+2^(j-1),j+2^(j-1)+2^(j-1)-1]两个等长度的区间(区间长度都是2^(j-1)),
    有了初始值和状态转移方程,我们可以自底向上递推出所有的f[i][j]的值。
    边界值的处理:
    由于区间最大长度为n,所以二维边界最大值为log(n)/log(2.0);
    一维边界为i+2^j-1<=n。
    查询:
    假设要查询区间[a,b]的最大值,由于区间的长度很可能不是2的整数幂,所以
    我们要把区间划分为长度为2的整数幂的两部分,而且这两个的并集必须是[a,b]。
    为了实现这个方案,我们需要先求出一个最大k,使得2^k<=(b-a+1),这样就可以把
    区间分为两部分[a,a+2^k-1]和[b-2^k+1,b],使它们既能不超过[a,b]区间的范围,又能
    把区间全部覆盖。于是,[a,b]区间的最大值就等于上述两个区间的最大值中最大的那个。
    方法描述
    #include <stdio.h>
    #include <math.h>
    #define MAXN 100
    #define max(a,b) (a > b ? a : b)
    const int num[MAXN];
    int dp[MAXN][20];
    void create_max (int n) {
    	int i,j,t;
    	for (i = 1;i <= n;i++) {
    		dp[i][0] = num[i];
    	}
    	t = (int)(log(n) / log(2.0));
    	for (j = 1;j <= t;j++) {
    		for (i = 1;i + (1 << j) - 1 <= n;i++) {
    			dp[i][j] = max(dp[i][j-1],dp[i+(1 << (j-1))][j-1]);
    		}
    	}
    }
     
    int get_max (int a,int b) {
    	int k = log(b - a + 1) / log(2.0);
    	return max(dp[a][k],dp[b-(1 << k) + 1][k]);
    }
    int main () {
    	int i,j,n,ans,a,b;
    	scanf ("%d",&n);
    	for (i = 1;i <= n;i++) {
    		scanf ("%d",num+i);
    	}
    	create_max (n);
    	scanf ("%d%d",&a,&b);
    	ans = get_max (a,b);
    	printf ("%d
    ",ans);
    	return 0;
    }
    

    POJ 3368 Frequent values

    题意:一个不降序列,让我们去区间询问区间最长连续相等序列。

    // #include<bits/stdc++.h>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring> // for memset
    #include <vector> // push_back() // vector<int>().swap(v);
    #include <set> //multiset set<int,greater<int>> //big->small
    #include <map>
    #include <stack> // top()
    #include <queue> // front() // priority_queue<T,vector<T>,greater<T> >
    #include <cmath> // auto &Name : STLName  Name.
    #include <utility>
    #include <sstream>
    #include <string> // __builtin_popcount (ans); // 获取某个数二进制位1的个数
    #include <cstdlib> // rand()
    
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0)
    #define lowbit(x) (x&(-x))
    using namespace std;
    typedef long long ll;
    
    const int maxn = 100010;
    int num[maxn],cnt[maxn],dp[maxn][25];
    int n,q,x,y;
    
    void ST(int n)
    {
        for(int i=1;i<=n;i++)
            dp[i][0]=cnt[i];
        for(int j=1;j<=24;j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
                dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    }
    
    int query(int i,int j)
    {
        int k=log(j-i+1.0)/log(2.0);
        return max(dp[i][k],dp[j-(1<<k)+1][k]);
    }
    
    int main(void)
    {
        while(~scanf("%d%d",&n,&q)&&n)
        {
            memset(cnt,0,sizeof(cnt));
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&num[i]);
                if(i==1)    cnt[i]=1;
                else if(num[i]==num[i-1])   cnt[i]=cnt[i-1]+1;
                else cnt[i]=1;
            }
            ST(n);
            while(q--)
            {
                scanf("%d%d",&x,&y);
                int now=x;
                while(now<=y&&num[now-1]==num[now])
                    now++;
                int ans=0;
                if(now<=y)
                    ans=query(now,y);
                ans=max(ans,now-x);
                printf("%d
    ",ans);
            }
        }
        
    
    	return 0;
    
    }
    

      

    POJ 1201 Intervals

    题意:一个整数集合Z有n个区间,每个区间有3个值,ai,bi,ci代表,在区间[ai,bi]上至少有ci个整数属于集合Z,ci可以在区间内任意取不重复的点。现在要满足所有区间的约束条件,问最少可选多少个点。

    思路:差分约束

    // #include<bits/stdc++.h>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring> // for memset
    #include <vector> // push_back() // vector<int>().swap(v);
    #include <set> //multiset set<int,greater<int>> //big->small
    #include <map>
    #include <stack> // top()
    #include <queue> // front() // priority_queue<T,vector<T>,greater<T> >
    #include <cmath> // auto &Name : STLName  Name.
    #include <utility>
    #include <sstream>
    
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0)
    #define lowbit(x) (x&(-x))
    using namespace std;
    typedef long long ll;
    
    const int maxn = 50005;
    const int inf = 0x3f3f3f3f;
    
    struct Edge
    {
        int v;
        int cost;
        Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
    };
    
    vector<Edge> E[maxn];
    
    void addedge(int u,int v,int w)
    {
        E[u].push_back(Edge(v,w));
    }
    
    bool vis[maxn];
    int cnt[maxn];
    int dist[maxn];
    
    bool SPFA(int start,int n,int *dist)
    {
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=n;i++)   dist[i]=-inf;
        vis[start]=true;
        dist[start]=0;
        queue<int>que;
    
        while(!que.empty()) que.pop();
    
        que.push(start);
        memset(cnt,0,sizeof(cnt));
        cnt[start]=1;
    
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            vis[u]=false;
    
            for(int i=0;i<E[u].size();i++)
            {
                int v=E[u][i].v;
                if(dist[v]<dist[u]+E[u][i].cost)
                {
                    dist[v]=dist[u]+E[u][i].cost;
                    if(!vis[v])
                    {
                        vis[v]=true;
                        que.push(v);
                        if(++cnt[v]>n) return false;
                    }
                }
            }
        }
        return true;
    }
    
    void init()
    {
        for(int i=0;i<maxn;i++)
            E[i].clear();
    }
    
    int main(void)
    {
        int n;
        while(~scanf("%d",&n))
        {
            init();
            int a,b,c,s=inf,e=-1;
            for(int i=0;i<n;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                addedge(a,b+1,c);
                s=min(s,a);
                e=max(e,b+1);
            }
            for(int i=s;i<e;i++)
            {
                addedge(i,i+1,0);
                addedge(i+1,i,-1);
            }
            SPFA(s,e,dist);
            printf("%d
    ",dist[e]);
        }
    
    	return 0;
    
    }
    

      还有一种方法就是贪心算法,用线段树优化

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define MMAX 50005
    using namespace std;
    int sum[MMAX*4],setv[MMAX*4];
    int t,_sum;
    struct node
    {
        int l,r,v;
    } line[MMAX];
    bool cmp(node a,node b)
    {
        return a.r<b.r;
    }
    void build(int rt,int L,int R)
    {
        if(L==R)
        {
            sum[rt]=1;
        }
        else
        {
            int M=(L+R)/2;
            build(rt*2,L,M);
            build(rt*2+1,M+1,R);
            sum[rt]=sum[rt*2]+sum[rt*2+1];
        }
    }
    void update(int rt,int L,int R,int ql,int qr)
    {
        if(t==0) return ;
     //   if(sum[rt]==0) return ;
        if(ql<=L&&qr>=R&&sum[rt]&&t>=sum[rt])
        {
            t-=sum[rt];
            sum[rt]=0;
            return ;
        }
        if(L==R) return ;
        if(sum[rt]==0)
        {
            int lc=rt*2,lr=rt*2+1;
            sum[lr]=sum[lc]=0;
        }
        int M=(L+R)/2;
        if(qr>M) update(rt*2+1,M+1,R,ql,qr);
        if(ql<=M) update(rt*2,L,M,ql,qr);
        sum[rt]=sum[rt*2+1]+sum[rt*2];
     
    }
    void query(int rt,int L,int R,int ql,int qr)
    {
        if(sum[rt]==0) return ;
        if(ql<=L&&qr>=R)
        {
            _sum+=sum[rt];
        }
        else
        {
             if(sum[rt]==0)
            {
            int lc=rt*2,lr=rt*2+1;
            sum[lr]=sum[lc]=0;
            }
            int M=(L+R)/2;
            if(ql<=M) query(rt*2,L,M,ql,qr);
            if(qr>M) query(rt*2+1,M+1,R,ql,qr);
        }
     
    }
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
     
            memset(setv,-1,sizeof(setv));
            int Max=0;
            for(int i=0; i<n; i++)
            {
                scanf("%d%d%d",&line[i].l,&line[i].r,&line[i].v);
                Max=max(Max,line[i].r);
            }
            build(1,0,Max);
            sort(line,line+n,cmp);
            for(int i=0; i<n; i++)
            {
                _sum=0;
                query(1,0,Max,line[i].l,line[i].r);
               // cout<<_sum<<endl;
                _sum=line[i].v-(line[i].r-line[i].l+1-_sum);
     
                if(_sum<=0) continue;
                t=_sum;
                update(1,0,Max,line[i].l,line[i].r);
     
            }
            _sum=0;
            query(1,0,Max,0,Max);
            printf("%d
    ",Max-_sum+1);
        }
        return 0;
    }
    

    POJ 3470 Walls

    题意:有N堵水平于坐标轴的墙,以及M只小鸟,小鸟一定会朝离自己最近的墙飞去撞晕自己,且只能飞向平行于坐标轴的 4个方向,撞到墙的边缘也算合理冲撞(即是说可以往延长线飞)。求各堵墙上各撞了几只小鸟。N, M <= 50000。

     思路:我们将所有的二维坐标离散,对xy方向分别进行扫描线,以y轴方向为例,我们先从y最小的线开始扫,如果是墙,那么在线段树中更新其在x轴上的分布位置,如果是鸟的坐标,那么在线段树中查找其位置,更新其答案。之后从y最大的开始反向扫描,更新,x方向也同理。

     

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <climits>
    using namespace std;
    const int N=50010;
    int n,m,wall,tot,dis[N],v[N],w[N],T[10*N],*arr;
    int x[3*N],y[3*N],ry[3*N],rx[3*N],r[3*N],xs[3*N],ys[3*N],px[3*N],py[3*N];
    bool cmp(int a,int b){return arr[a]<arr[b];}
    void compress(int *x,int *r,int n,int *a,int *mp){
        for(int i=0;i<n;i++)r[i]=i;
        arr=x; sort(r,r+n,cmp);
        int m=-1; a[r[0]]=++m; mp[m]=x[r[0]];
        for(int i=1;i<n;i++){
            int cur=r[i],lst=r[i-1];
            if(x[cur]==x[lst])a[cur]=m;
            else a[cur]=++m,mp[m]=x[cur];
        }
    }
    int query(int q,int x,int l,int r){
        if(T[x]!=-2)return T[x];
        int mid=(l+r)>>1;
        if(q<mid)return query(q,x<<1|1,l,mid);
        return query(q,x+1<<1,mid,r);
    }
    void update(int a,int b,int x,int l,int r,int val){
        if(r<=a||b<=l)return;
        else if(a<=l&&r<=b)T[x]=val;
        else{
            if(T[x]!=-2){
                T[x+1<<1]=T[x<<1|1]=T[x];
                T[x]=-2;
            }int mid=(l+r)>>1;
            update(a,b,x<<1|1,l,mid,val);
            update(a,b,x+1<<1,mid,r,val);
        }
    }
    void scan(int k,int *ys,int *xs,int *py,int W){
        if(k<wall){
            int _k=k^1;
            if(xs[_k]>=xs[k])update(xs[k],xs[_k]+1,0,0,W,k/2);
        }else{
            int t=query(xs[k],0,0,W);
            if(~t){
                int d=min(abs(py[ys[k]]-py[ys[t*2]]),abs(py[ys[k]]-py[ys[t*2+1]]));
                k-=wall; if(d<dis[k])dis[k]=d,v[k]=t;
            }
        }
    }
    void fly(int *ry,int *ys,int *xs,int *py,int W){
        T[0]=-1; for(int i=0;i<tot;i++)scan(ry[i],ys,xs,py,W);
        T[0]=-1; for(int i=tot-1;i>=0;i--)scan(ry[i],ys,xs,py,W);
    }
    int main(){
        scanf("%d%d",&n,&m);
        wall=2*n; tot=wall+m;
        for(int i=0;i<tot;i++)scanf("%d%d",x+i,y+i);
        compress(x,rx,tot,xs,px);
        compress(y,ry,tot,ys,py);
        fill(dis,dis+m,INT_MAX);memset(w,0,n);
        fly(ry,ys,xs,py,xs[rx[tot-1]]+1);
        fly(rx,xs,ys,px,ys[ry[tot-1]]+1);
        for(int i=0;i<m;i++)++w[v[i]];
        for(int i=0;i<n;i++)printf("%d
    ",w[i]);
        return 0;
    }
    

      

    UVA 11990 ''Dynamic'' Inversion

    题意:给你n个数,然后有m次操作,每次操作可以删除一个数, 然后问你每次删除之前,还有多少个逆序对

  • 相关阅读:
    AD20改变pcb图纸大小方式
    ceph相关概念
    五种IO模型和三种实现方式
    MongoDB入门
    GO通过sqlx库操作MySQL
    Go原生sql操作MySQL
    Traefik工作原理
    Redis主从
    Nginx入门
    Redis入门
  • 原文地址:https://www.cnblogs.com/jaszzz/p/12945121.html
Copyright © 2011-2022 走看看