Diorvh

导航

【日记】12.3

12.3日记

线段树

染色问题困扰好久……

  1. 洛谷P2161。安排约定问题。

思路:这道题其实应该是用平衡树的。不过如果涉及到平衡树,那么就考虑能不能直接套用set。既然有只保留一个的特性,那么就可以利用set只保留一个的特性,让冲突的设计重载运算符<让其相等,即可进行去重。由于每个元素只会进出一次,因此复杂度是正确的。

注意:重载set运算符要这么干:

struct Date{
    int l,r;
    Date(int a=0,int b=0):l(a),r(b){}
    bool operator<(const Date &x)const{
        return r<x.l;
    }
};
set<Date> st;

多加const。

染色解法感觉太麻烦了。

//插入区间并删除冲突+询问区间个数
#include<bits/stdc++.h>
using namespace std;
struct Date{
    int l,r;
    Date(int a=0,int b=0):l(a),r(b){}
    bool operator<(const Date &x)const{
        return r<x.l;
    }
};
set<Date> st;
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        char s[3];
        scanf("%s",s);
        if (s[0]=='B')
            printf("%d\n",st.size());
        else{
            int l,r,last=st.size();
            scanf("%d%d",&l,&r);
            set<Date>::iterator it=st.find(Date(l,r));
            while(it!=st.end())
                st.erase(it),it=st.find(Date(l,r));
            printf("%d\n",last-st.size());
            st.insert(Date(l,r));
        }
    }
    return 0;
}

  1. POJ2777,HDU5023。线段树染色问题。

功能:区间修改+区间查询不同数个数。不同颜色个数较少。

构造:状压每个颜色。v[id]表示当前区间所有颜色(状压)。维护区间值的|值。用lazy减少修改次数保证单次logn。

注意:多组数据清空啊……

POJ2777:

//区间修改+区间不同数个数(少)
#include<cstdio>
#include<algorithm>
#define mid (l+r)/2
using namespace std;
const int M=1e5+20;
int v[4*M],lazy[4*M];
inline void push_up(int id){
    v[id]=v[id*2]|v[id*2+1];
}
inline void push_down(int id){
    if (lazy[id])
        lazy[id*2]=lazy[id*2+1]=v[id*2]=v[id*2+1]=lazy[id],
        lazy[id]=0;
}
void build(int id,int l,int r){
    if (l==r){
        v[id]=2;
        return;
    }
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    push_up(id);
}
void operate(int id,int l,int r,int ql,int qr,int x){
    if (ql<=l&&r<=qr){
        v[id]=lazy[id]=x;
        return;
    }
    push_down(id);
    if (ql<=mid)
        operate(id*2,l,mid,ql,qr,x);
    if (mid<qr)
        operate(id*2+1,mid+1,r,ql,qr,x);
    push_up(id);
}
int query(int id,int l,int r,int ql,int qr){
    if (ql<=l&&r<=qr)
        return v[id];
    push_down(id);
    int sum=0;
    if (ql<=mid)
        sum|=query(id*2,l,mid,ql,qr);
    if (mid<qr)
        sum|=query(id*2+1,mid+1,r,ql,qr);
    return sum;
}
int main(){
    int l,t,o;
    scanf("%d%d%d",&l,&t,&o);
    build(1,1,l);
    for(int i=1;i<=o;++i){
        char s[2];
        int a,b,c;
        scanf("%s",s);
        if (s[0]=='C'){
            scanf("%d%d%d",&a,&b,&c);
            operate(1,1,l,min(a,b),max(a,b),1<<c);
        }
        else{
            scanf("%d%d",&a,&b);
            int ca=query(1,1,l,min(a,b),max(a,b)),ans=0;
            while(ca)
                ans+=(ca&1),ca>>=1;
            printf("%d\n",ans);
        }
    }
    return 0;
}

HDU5023:

//线段树染色升级版
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=1e6+20;
int v[4*M],lazy[4*M];
inline void push_up(int id){
    v[id]=v[id*2]|v[id*2+1];
}
inline void push_down(int id){
    if (lazy[id])
        lazy[id*2]=lazy[id*2+1]=v[id*2]=v[id*2+1]=lazy[id],
        lazy[id]=0;
}
void build(int id,int l,int r){
	lazy[id]=0;
    if (l==r){
        v[id]=(1<<2);
        return;
    }
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    push_up(id);
}
void operate(int id,int l,int r,int ql,int qr,int x){
    if (ql<=l&&r<=qr){
        v[id]=lazy[id]=x;
        return;
    }
    push_down(id);
    if (ql<=mid)
        operate(id*2,l,mid,ql,qr,x);
    if (mid<qr)
        operate(id*2+1,mid+1,r,ql,qr,x);
    push_up(id);
}
int query(int id,int l,int r,int ql,int qr){
    if (ql<=l&&r<=qr)
        return v[id];
    push_down(id);
    int sum=0;
    if (ql<=mid)
        sum|=query(id*2,l,mid,ql,qr);
    if (mid<qr)
        sum|=query(id*2+1,mid+1,r,ql,qr);
    return sum;
}
int main(){
    int l,o;
    while(~scanf("%d%d",&l,&o)){
    	if (l==0&&o==0)
    		break;
	    build(1,1,l);
	    for(int i=1;i<=o;++i){
	        char s[2];
	        int a,b,c;
	        scanf("%s",s);
	        if (s[0]=='P'){
	            scanf("%d%d%d",&a,&b,&c);
	            operate(1,1,l,a,b,1<<c);
	        }
	        else{
	            scanf("%d%d",&a,&b);
	            int ca=query(1,1,l,a,b);
	            vector<int> v;
	            for(int j=1;j<=30;++j)
	                if((1<<j)&ca)
	                    v.push_back(j);
	            printf("%d",v[0]);
	            for(int j=1;j<v.size();++j)
	                printf(" %d",v[j]);
	            putchar('\n');
	        }
	    }
	}
    return 0;
}

  1. HDU1166。单点加减+区间求和。

比较简单,写的很快。

//单点加减+区间求和
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=5e4+20;
int v[4*M],a[M];
inline void push_up(int id){
    v[id]=v[id*2]+v[id*2+1];
}
void build(int id,int l,int r){
    if (l==r){
        v[id]=a[l];
        return;
    }
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    push_up(id);
}
void operate(int id,int l,int r,int pos,int x){
    if (l==r){
        v[id]+=x;
        return;
    }
    if (pos<=mid)
        operate(id*2,l,mid,pos,x);
    else
        operate(id*2+1,mid+1,r,pos,x);
    push_up(id);
}
int query(int id,int l,int r,int ql,int qr){
    if (ql<=l&&r<=qr)
        return v[id];
    int ans=0;
    if (ql<=mid)
        ans+=query(id*2,l,mid,ql,qr);
    if (mid<qr)
        ans+=query(id*2+1,mid+1,r,ql,qr);
    return ans;
}
int main(){
    int T;
    scanf("%d",&T);
    for(int z=1;z<=T;++z){
        printf("Case %d:\n",z);
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]);
        build(1,1,n);
        char s[10];
        scanf("%s",s);
        while(s[0]!='E'){
            int b,c;
            scanf("%d%d",&b,&c);
            if (s[0]=='Q')
                printf("%d\n",query(1,1,n,b,c));
            else if (s[0]=='A')
                operate(1,1,n,b,c);
            else if (s[0]=='S')
                operate(1,1,n,b,-c);
            scanf("%s",s);
        }
    }
    return 0;
}
  1. HDU1754。单点修改+区间查询最大值
//单点修改+区间最大值
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=2e5+20;
int v[4*M],a[M];
inline void push_up(int id){
    v[id]=max(v[id*2],v[id*2+1]);
}
void build(int id,int l,int r){
    if (l==r){
        v[id]=a[l];
        return;
    }
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    push_up(id);
}
void operate(int id,int l,int r,int pos,int x){
    if (l==r){
        v[id]=x;
        return;
    }
    if (pos<=mid)
        operate(id*2,l,mid,pos,x);
    else
        operate(id*2+1,mid+1,r,pos,x);
    push_up(id);
}
int query(int id,int l,int r,int ql,int qr){
    if (ql<=l&&r<=qr)
        return v[id];
    int ans=0;
    if (ql<=mid)
        ans=max(ans,query(id*2,l,mid,ql,qr));
    if (mid<qr)
        ans=max(ans,query(id*2+1,mid+1,r,ql,qr));
    return ans;
}
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]);
        build(1,1,n);
        for(int i=1;i<=m;++i){
            char s[3];
            scanf("%s",s);
            int a,b;
            scanf("%d%d",&a,&b);
            if (s[0]=='Q')
                printf("%d\n",query(1,1,n,a,b));
            else
                operate(1,1,n,a,b);
        }
    }
    return 0;
}
  1. HDU1698。区间修改+区间求和。

一个是注意清空,另外一个是pushdown操作,下方的时候要乘区间长度。以后还是按照大佬的那套吧,确实会更方便一些。

//区间修改+区间和
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=1e5+20;
int v[4*M],lazy[4*M];
inline void pushup(int id){
    v[id]=v[id*2]+v[id*2+1];
}
inline void pushdown(int id,int l,int r){
    if (lazy[id])
        lazy[id*2]=lazy[id*2+1]=lazy[id],
        v[id*2]=lazy[id]*(mid-l+1),
        v[id*2+1]=lazy[id]*(r-mid),
        lazy[id]=0;
}
void build(int id,int l,int r){
    lazy[id]=0;
    if (l==r){
        v[id]=1;
        return;
    }
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    pushup(id);
}
void operate(int id,int l,int r,int ql,int qr,int x){
    if (ql<=l&&r<=qr){
        v[id]=x*(r-l+1),lazy[id]=x;
        return;
    }
    pushdown(id,l,r);
    if (ql<=mid)
        operate(id*2,l,mid,ql,qr,x);
    if (mid<qr)
        operate(id*2+1,mid+1,r,ql,qr,x);
    pushup(id);
}
int query(int id,int l,int r,int ql,int qr){
    if (ql<=l&&r<=qr)
        return v[id];
    pushdown(id,l,r);
    int sum=0;
    if (ql<=mid)
        sum+=query(id*2,l,mid,ql,qr);
    if (mid<qr)
        sum+=query(id*2+1,mid+1,r,ql,qr);
    return sum;
}
int main(){
    int T;
    scanf("%d",&T);
    for(int z=1;z<=T;++z){
        int n,q;
        scanf("%d%d",&n,&q);
        build(1,1,n);
        for(int i=1;i<=q;++i){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            operate(1,1,n,a,b,c);
        }
        printf("Case %d: The total value of the hook is %d.\n",z,query(1,1,n,1,n));
    }
    return 0;
}
  1. OpenJ2299。n个不同数组成的数列,问有多少个逆序对。

模板:一维偏序。可以归并,也可以树状数组。首先离散化,之后从后往前,将a[i]置为1,将1-i-1的区间和加入答案即可。注意开LL!

//单点修改+区间查询和
#include<bits/stdc++.h>
using namespace std;
const int M=5e5+20;
int num_bit,a[M],b[M],c[M];
unordered_map<int,int> rev;
inline int lowbit(int x){return x&(-x);}
inline void update(int x,int k){
    while(x<=num_bit)
        c[x]+=k,x+=lowbit(x);
}
inline int query(int x){//1-x的和
    int ans=0;
    while(x)
        ans+=c[x],x-=lowbit(x);
    return ans;
}
int main(){
    int n;
    scanf("%d",&n);
    while(n){
        num_bit=n;
        for (int i=1;i<=n;++i)
            scanf("%d",&a[i]),b[i]=a[i],c[i]=0;
        rev.clear(); 
        sort(a+1,a+n+1);
        for(int i=1;i<=n;++i)
            rev[a[i]]=i;
        long long ans=0;
        for(int i=n;i>=1;--i)
            ans+=query(rev[b[i]]),update(rev[b[i]],1);
        printf("%lld\n",ans);
        scanf("%d",&n);
    }
    return 0;
}

posted on 2019-12-04 01:47  diorvh  阅读(215)  评论(0编辑  收藏  举报