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次操作,每次操作可以删除一个数, 然后问你每次删除之前,还有多少个逆序对

  • 相关阅读:
    hdu 5646 DZY Loves Partition
    bzoj 1001 狼抓兔子 平面图最小割
    poj 1815 Friendship 最小割 拆点 输出字典序
    spoj 1693 Coconuts 最小割 二者取其一式
    hdu 5643 King's Game 约瑟夫环变形
    约瑟夫环问题
    hdu 5642 King's Order
    CodeForces 631C Report
    1039: C语言程序设计教程(第三版)课后习题9.4
    1043: C语言程序设计教程(第三版)课后习题10.1
  • 原文地址:https://www.cnblogs.com/jaszzz/p/12945121.html
Copyright © 2011-2022 走看看