2018 UESTC Training For Data Structures

阅读须知:以下代码有一些是参考别人的代码或者题解,然后自己再码出来的,所以各路大神如果发现代码和自己的很相似,没错,那就是你的代码(逃。


A.一棵简单的线段树

题目链接
分析:操作涉及点修改、区间求和,维护线段树每个结点的最大值,最小值,区间和,然后就是很裸的一颗线段树了。
线段树入门

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e6+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,m;

struct node{
    LL sum, maxn, minn;
    //node (LL sum=0, LL maxn=0, LL minn=0):sum(sum),maxn(maxn),minn(minn){}
}tree[N<<2];


void build(int L, int R, int rt){
    if(L==R) tree[rt].sum = tree[rt].maxn = tree[rt].minn = 0;
    else{
        int mid = (L+R)>>1;
        build(L,mid,rt<<1);
        build(mid+1,R,rt<<1|1);
        //pushup(tree[rt],tree[rt<<1],tree[rt<<1|1]);
    }
}

node query(int l, int r, int L, int R, int rt){
    if(l<=L&&R<=r) return tree[rt];
    else{
        int mid = (L+R)>>1;
        if(r<=mid) return query(l,r,L,mid,rt<<1);
        else if(l>mid) return query(l,r,mid+1,R,rt<<1|1);
        else{
            node u, v, ans;
            u = query(l,mid,L,mid,rt<<1);
            v = query(mid+1,r,mid+1,R,rt<<1|1);
            ans.sum = u.sum+v.sum;
            ans.maxn = max(u.maxn,v.maxn);
            ans.minn = min(u.minn,v.minn);
            return ans;
        }
    }
}

void update(int l, int r, int L, int R, int rt, LL val){
    if(L==l&&r==R) tree[rt].sum = tree[rt].maxn = tree[rt].minn = val;
    else{
        int mid = (L+R)>>1;
        if(r<=mid) update(l,r,L,mid,rt<<1,val);
        else if(l>mid) update(l,r,mid+1,R,rt<<1|1,val);
        tree[rt].sum = tree[rt<<1].sum+tree[rt<<1|1].sum;
        tree[rt].maxn = max(tree[rt<<1].maxn,tree[rt<<1|1].maxn);
        tree[rt].minn = min(tree[rt<<1].minn,tree[rt<<1|1].minn);
    }
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d%d",&n,&m);
    build(1,n,1);
    for(int i = 0; i < m; i++){
        int op,l,r;
        LL x;
        scanf("%d", &op);
        if(op){
            scanf("%d%d",&l,&r);
            node v = query(l,r,1,n,1);
            LL ans = v.sum-v.maxn-v.minn;
            printf("%lld\n", ans);
        }
        else{
            scanf("%d%lld",&l,&x);
            update(l,l,1,n,1,x);
        }
    }
    return 0;
}

B.一棵普通的线段树

题目链接
分析:操作涉及区间修改,区间求和,线段树结点的区间和,而对于区间修改,则需要加个懒惰标记(修改标记)

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e6+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,m;

struct node{
    LL sum, add;
    //node (LL sum=0, LL maxn=0, LL minn=0):sum(sum),maxn(maxn),minn(minn){}
}tree[N<<2];


void build(int L, int R, int rt){
    if(L==R) tree[rt].sum = tree[rt].add = 0;
    else{
        int mid = (L+R)>>1;
        build(L,mid,rt<<1);
        build(mid+1,R,rt<<1|1);
        //pushup(tree[rt],tree[rt<<1],tree[rt<<1|1]);
    }
}

void pushdown(int rt, int ln, int rn){
    if(tree[rt].add){
        tree[rt<<1].sum+=tree[rt].add*ln;
        tree[rt<<1|1].sum+=tree[rt].add*rn;
        tree[rt<<1].add+=tree[rt].add;
        tree[rt<<1|1].add+=tree[rt].add;
        tree[rt].add = 0;
    }
}

node query(int l, int r, int L, int R, int rt){
    if(l<=L&&R<=r) return tree[rt];
    else{
        int mid = (L+R)>>1;
        pushdown(rt,mid-L+1,R-mid);
        if(r<=mid) return query(l,r,L,mid,rt<<1);
        else if(l>mid) return query(l,r,mid+1,R,rt<<1|1);
        else{
            node u, v, ans;
            u = query(l,mid,L,mid,rt<<1);
            v = query(mid+1,r,mid+1,R,rt<<1|1);
            ans.sum = u.sum+v.sum;
            return ans;
        }
    }
}

void update(int l, int r, int L, int R, int rt, LL val){
    if(l<=L&&R<=r){
        tree[rt].sum+=(R-L+1)*val;
        tree[rt].add+=val;
        return ;
    }
    else{
        int mid = (L+R)>>1;
        pushdown(rt,mid-L+1,R-mid);
        if(r<=mid) update(l,r,L,mid,rt<<1,val);
        else if(l>mid) update(l,r,mid+1,R,rt<<1|1,val);
        else{
            update(l,mid,L,mid,rt<<1,val);
            update(mid+1,r,mid+1,R,rt<<1|1,val);
        }
        tree[rt].sum = tree[rt<<1].sum+tree[rt<<1|1].sum;
    }
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d%d",&n,&m);
    build(1,n,1);
    for(int i = 0; i < m; i++){
        int op,l,r;
        LL x;
        scanf("%d", &op);
        if(op){
            scanf("%d%d%lld",&l,&r,&x);
            node v = query(l,r,1,n,1);
            printf("%lld\n", v.sum);
        }
        else{
            scanf("%d%d%lld",&l,&r,&x);
            update(l,r,1,n,1,x);
        }
    }
    return 0;
}

C.一棵像样的线段树

题目链接
分析:给出c数组,通过ci求b数组的mex,mex就是表示集合中最小的未出现的正整数,即$b(i) = mex{b(j)},j的范围(i-c(i) ~ i-1)对于这个如果b(i-ci)不是1的话,那bi就可以选1,由此可以看出我们只需要把b(i)可选的范围分成两部分1 ~ i-c(i),i-c(i) ~ n,看到分成两部分,就可以想到用线段树(逃,接下来就是关键部分了,因为对于一个b(i),可能前面的c(i)个b都选了1~i-c(i),所以bi的中间值(数学老师常说的假设),设一个值v=i-c(i)来进行查找,小于v就往左区间找,大于或等于就往右区间找,线段树结点维护对于值x最后出现的位置,(这个维护可能有点难理解,就是比如x=1,然后这个时候他最后出现的位置是在3,而你在算一个b(4),b(4)对应c为2,则b(4)=mex{b(2),b(3)},而你设置的查找值是v=i-c(i)=2,看左边,x=1,位置3>v,所以不能往左边,要往右边找,差不多是这个道理(一大堆废话)),每次找出一个b(i)后,就更新b(i)最后出现的位置,嗯,就这样。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e6+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,c[N];
int tree[N<<2];

void update(int L, int R, int rt, int pos, int val){
    if(L==R){
        tree[rt] = val;
        return ;
    }
    int mid = (L+R)>>1;
    if(pos<=mid) update(L,mid,rt<<1,pos,val);
    else update(mid+1,R,rt<<1|1,pos,val);
    tree[rt] = min(tree[rt<<1],tree[rt<<1|1]);
}

int query(int L, int R, int rt, int pos){
    if(L==R) return L;
    int mid = (L+R)>>1;
    if(tree[rt<<1]<pos) return query(L,mid,rt<<1,pos);
    else return query(mid+1,R,rt<<1|1,pos);
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) scanf("%d",&c[i]);
    mst(tree,-1);
    update(1,n+1,1,1,0);
    for(int i = 1; i <= n; i++){
        int ans = query(1,n+1,1,i-c[i]);
        update(1,1+n,1,ans,i);
        printf("%d ",ans);
    }
    return 0;
}

D.一棵复杂的线段树

题目链接
分析:题目给一个序列,若干个操作,每个操作是对一个区间进行升序或者降序排序操作,问经过这么多次操作之后第k个元素是什么。(好难),这个时候要靠猜,猜猜这个序列那个才是正确的答案,那就随便猜一个,比如k位置的元素,就叫x吧。好那要看这个元素是否合法呢,就可以以这个数为基准,小于这个元素的值置为0,否则为1,为什么要这么做呢,因为这样有利于用线段树实现排序操作,首先要明确一点,我们只需要知道这个位置的值是否小于x,它具体的值是多少我们不用管,比如对一个区间(0,0,1,0,1)进行升序,就变成了(0,0,0,1,1),降序就是(1,1,0,0,0),回到用线段树实现排序操作,线段树的结点维护这个区间有多少个1,比如有g个,这样升序排序就是对后g个元素置为1,降序就是对前面g个元素置为1,置1操作就是线段树的区间更新。好了讲完用线段树操作之后怎么办,没错,就是检验这个x是否合法,如果k位置为1就说明可能大于等于x,这是合法的,0是小于,所以不合法。那么这样xjb猜太满,而且有很多种合法情况,所以我们就要用二分找答案的方法,如果一顿操作之后k位置为1,那么说明这个数可能是偏小的(如果选的x很大数组里面1就会很少之类的),于是往大的数找,反之亦然。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

struct node{
    int op,X,Y;
}gg[N];

int n,k,m;
int a[N],b[N];
int tree[N<<2],add[N<<2];

void build(int L, int R, int rt, int val){
    add[rt] = -1;
    if(L==R) tree[rt] = (a[L]>=val)?1:0;
    else{
        int mid = (L+R)>>1;
        build(L,mid,rt<<1,val);
        build(mid+1,R,rt<<1|1,val);
        tree[rt] = tree[rt<<1] + tree[rt<<1|1];
    }
}

void pushdown(int L, int R, int mid, int rt){
    tree[rt<<1] = (mid-L+1)*add[rt];
    tree[rt<<1|1] = (R-mid)*add[rt];
    add[rt<<1] = add[rt<<1|1] = add[rt];
    add[rt] = -1;
}

int query1(int L, int R, int l, int r, int rt){
    if(l<=L&&R<=r) return tree[rt];
    int mid = (L+R)>>1;
    int ans = 0;
    if(add[rt]!=-1) pushdown(L,R,mid,rt);
    if(l<=mid) ans+=query1(L,mid,l,r,rt<<1);
    if(r>mid) ans+=query1(mid+1,R,l,r,rt<<1|1);
    return ans;
}

int query2(int L, int R, int rt, int val){
    if(L==R) return tree[rt];
    int mid = (L+R)>>1;
    if(add[rt]!=-1) pushdown(L,R,mid,rt);
    if(val<=mid) return query2(L,mid,rt<<1,val);
    else return query2(mid+1,R,rt<<1|1,val);
}

void update(int L, int R, int l, int r, int rt, int val){
    if(l<=L&&R<=r){
        tree[rt] = (R-L+1)*val;
        add[rt] = val;
        return ;
    }
    int mid = (L+R)>>1;
    if(add[rt]!=-1) pushdown(L,R,mid,rt);
    if(l<=mid) update(L,mid,l,r,rt<<1,val);
    if(mid+1<=r) update(mid+1,R,l,r,rt<<1|1,val);
    tree[rt] = tree[rt<<1]+tree[rt<<1|1];
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d%d",&n,&k);
    for(int i = 1; i <= n; i++){
        scanf("%d",&a[i]);
        b[i] = i;
    }
    //sort(b+1,b+1+n);
    scanf("%d",&m);
    for(int i = 0; i < m; i++) scanf("%d%d%d",&gg[i].op,&gg[i].X,&gg[i].Y);
    int L = 1, R = n, mid;
    while(L<R){
        mid = (L+R)>>1;
        build(1,n,1,mid+1);
        for(int i = 0; i < m; i++){
            int num = query1(1,n,gg[i].X,gg[i].Y,1);//cout<<"yes"<
            if(num==0) continue;
            if(gg[i].op==1){
                update(1,n,gg[i].X,gg[i].Y,1,0);
                update(1,n,gg[i].X,gg[i].X+num-1,1,1);
            }
            else{
                update(1,n,gg[i].X,gg[i].Y,1,0);
                update(1,n,gg[i].Y-num+1,gg[i].Y,1,1);
            }
        }
        if(query2(1,n,1,k)) L = mid+1;
        else R = mid;
    }
    printf("%d\n",L);
    return 0;
}

E.小埋的steam愿望单

题目链接
分析:有好多种操作,修改、删除、插入,(跟课设好像),对于每个询问输出最贵或者最便宜的,那么就要维护一个有序的集合,这个集合有要方便的用这些操作,只有STL的set莫属啦。(注意题干的重要事情说三遍)

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n;

struct node{
    string name;
    int V;
    bool operator < (const node &t) const{
        if(V==t.V) return name<t.name;
        return V<t.V;
    }
}gg[N];

set<node> st;
map<string,int> mp;

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d", &n);
    string thing;
    int val,op,cnt = 0;
    for(int i = 0; i < n; i++){
        cin>>op;
        if(op==1){
            cin>>thing>>val;
            if(mp.count(thing)) continue;
            gg[cnt].name = thing;
            gg[cnt].V = val;
            st.insert(gg[cnt]);
            mp[thing] = cnt;
            cnt++;
        }
        else if(op==2){
            cin>>thing;
            if(!mp.count(thing)) continue;
            st.erase(gg[mp[thing]]);
            mp.erase(thing);
        }
        else if(op==3){
            cin>>thing>>val;
            if(!mp.count(thing)) continue;
            st.erase(gg[mp[thing]]);
            gg[mp[thing]].V = val;
            st.insert(gg[mp[thing]]);
        }
        else if(op==4){
            cin>>val;
            if(st.empty()) continue;
            if(val==1){
                set<node>::iterator it = st.begin();
                node u = *it;
                cout<<u.name<<endl;
            }
            else{
                set<node>::iterator it = st.end();
                it--;
                node u = *it;
                cout<<u.name<<endl;
            }
        }
    }
    return 0;
}

F.好吃不过饺子

题目链接
分析:给出一维平面上的若干个点,每个点有权值(题目给出的点是有序的),接下来有若干个询问,这个序列中的每个数是否大于或者小于这个数(位置~位置-len)范围的min,avg, max,是就ans++(有点绕,看题干就懂了)。这道题有多种解法,我们可以用队列+线段树维护,队列维护这个数对应的检验范围,然后用线段树对这个范围的数进行查找,线段树结点维护区间的min,max还有和,(没有码出来);另一种是用单调队列,找某个区间的min,就维护一个单调上升队列,找某个区间的max,就维护一个单调下降的队列,队列维护的时候,检验每个数,就是把队列中超过区间范围的数pop掉,然后就可以开心地维护单调队列了。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,c,ans;

struct node{
    int X,Y;
}gg[N];

deque<node> Q;

void Deal(bool g_or_l, bool a_or_i, int len){   //g_or_l true is g, false is l; a_or_i true is a, false is i
    for(int i = 0; i < n; i++){
        while(!Q.empty()&&Q.front().X<gg[i].X-len) Q.pop_front();
        if(Q.empty()) Q.push_back(gg[i]);
        else{
            if(g_or_l){
                if(a_or_i){
                    if(Q.front().Y<gg[i].Y) ans++;
                    while(!Q.empty()&&Q.back().Y<=gg[i].Y) Q.pop_back();
                    Q.push_back(gg[i]);
                }
                else{
                    if(Q.front().Y<gg[i].Y) ans++;
                    while(!Q.empty()&&Q.back().Y>=gg[i].Y) Q.pop_back();
                    Q.push_back(gg[i]);
                }
            }
            else{
                if(a_or_i){
                    if(Q.front().Y>gg[i].Y) ans++;
                    while(!Q.empty()&&Q.back().Y<=gg[i].Y) Q.pop_back();
                    Q.push_back(gg[i]);
                }
                else{
                    //printf("yes\n");
                    //printf("%d %d\n",Q.front().Y,gg[i].Y);
                    if(Q.front().Y>gg[i].Y) ans++;
                    while(!Q.empty()&&Q.back().Y>=gg[i].Y) Q.pop_back();
                    Q.push_back(gg[i]);
                }
            }
        }
        //printf("%d\n",i);
    }
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d%d",&n,&c);
    for(int i = 0; i < n; i++) scanf("%d%d",&gg[i].X,&gg[i].Y);
    for(int i = 0; i < c; i++){
        char K[10], fun[10];
        int len;
        ans = 0;
        scanf("%s%s%d",K,fun,&len);
        while(!Q.empty()) Q.pop_back();
        if(K[0]=='g'){
            if(fun[1]=='a') Deal(true,true,len);
            else if(fun[1]=='i') Deal(true,false,len);
            else{
                int sum = 0;
                for(int i = 0; i < n; i++){
                    while(!Q.empty()&&Q.front().X<gg[i].X-len) {
                        sum-=Q.front().Y;
                        Q.pop_front();
                    }
                    if(Q.empty()) {
                        Q.push_back(gg[i]);
                        sum+=gg[i].Y;
                    }
                    else{
                        if((double)gg[i].Y-(double)sum/Q.size()>1e-3) ans++;
                        Q.push_back(gg[i]);
                        sum+=gg[i].Y;
                    }
                }
            }
        }
        else{
            if(fun[1]=='a') Deal(false,true,len);
            else if(fun[1]=='i') Deal(false,false,len);
            else{
                int sum = 0;
                for(int i = 0; i < n; i++){
                    while(!Q.empty()&&Q.front().X<gg[i].X-len) {
                        sum-=Q.front().Y;
                        Q.pop_front();
                    }
                    if(Q.empty()) {
                        Q.push_back(gg[i]);
                        sum+=gg[i].Y;
                    }
                    else{
                        if((double)gg[i].Y-(double)sum/Q.size()<1e-3) ans++;
                        Q.push_back(gg[i]);
                        sum+=gg[i].Y;
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

G.三澄美琴的心里只有学习

题目链接
分析:很明显这就是维护一个队列的操作(好像跟课设又是很像?),注意t是升序输入,所以对于每个2,3操作,直接把队首超过t时间的出队(万能的STL)

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n;

struct node{
    int ID,x,y;
    node (int ID=0, int x=0, int y=0):ID(ID),x(x),y(y){}
};

queue<node> Q;

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d", &n);
    for(int i = 0; i < n; i++){
        int op,t,a,b;
        scanf("%d", &op);
        if(op==1){
            scanf("%d%d%d",&t,&a,&b);
            node u(a,t,t+b-1);
            Q.push(u);
        }
        else if(op==2){
            scanf("%d",&t);
            while(!Q.empty()){
                node u = Q.front(); Q.pop();
                if(u.y>=t) break;
            }
        }
        else if(op==3){
            scanf("%d",&t);
            if(Q.empty()) printf("-1\n");
            else{
                while(!Q.empty()){
                    node u = Q.front();
                    if(u.y>=t) break;
                    Q.pop();
                }
                if(Q.empty()) printf("-1\n");
                else {
                    node u = Q.front();
                    printf("%d\n",u.ID);
                }
            }
        }
    }
    return 0;
}

H.中堂系的困难任务

题目链接
分析:发现式子就是哈夫曼树的递推式,所以直接优先队列算术来,至于为什么是哈夫曼树
哈夫曼树学习

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

priority_queue<LL,vector<LL>,greater<LL> > pq;
LL x;

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    int T,n; scanf("%d",&T);
    while(T--){
        while(!pq.empty()) pq.pop();
        scanf("%d",&n);
        for(int i = 0; i < n; i++){
            scanf("%lld",&x);
            pq.push(x);
        }
        LL ans = 0;
        while(pq.size()>1){
            LL temp = pq.top();
            pq.pop();
            temp+=pq.top();
            pq.pop();
            ans+=temp;
            pq.push(temp);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

I.不如把并查集加上个计数功能吧

题目链接
分析:简单又基础的并查集。
并查集入门

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,m;
int pre[N],cnt[N];

int fin(int x){
    if(pre[x]==x) return x;
    else return pre[x] = fin(pre[x]);
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++) pre[i] = i;
    for(int i = 0; i < m; i++){
        int x,y,dx,dy;
        scanf("%d%d",&x,&y);
        dx = fin(x); dy = fin(y);
        if(dx!=dy) pre[dy] = dx;
    }
    for(int i = 1; i <= n; i++) cnt[fin(i)]++;
    int temp,t,ans = 0; scanf("%d",&temp);
    while(temp--){
        scanf("%d", &t);
        printf("%d\n",cnt[fin(t)]);
    }
    return 0;
}

J.老头马桶枪!

题目链接
分析:有2种不同的信息,同类和克制,有三种,所以我们就给多两个个对立(克制)的集合,然后对每个点拆成x,x+100000,x+200000,表示在不同的克制集合里面,克制的顺序可以看成一个圈(好像本来就是这样子),然后每次给出克制的信息就等于把圈转一转,因为有克制顺序(没有理解的话直接看代码)?用并查集维护。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,m;
int pre[300100];

int fin(int x){
    return (pre[x]==x)?x:(pre[x] = fin(pre[x]));
}

void link(int x, int y){
    int dx = fin(x);
    int dy = fin(y);
    if(dx!=dy) pre[dx] = dy;
}

bool judge(int x, int y){
    int dx = fin(x);
    int dy = fin(y);
    if(dx!=dy) return true;
    else return false;
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d%d", &n,&m);
    for(int i = 1; i <= n+200000; i++) pre[i] = i;
    int flag = -1;
    for(int i = 1; i <= m; i++){
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if(op==2){
            if(judge(x,y)&&judge(x+100000,y+100000)&&judge(x+200000,y+200000)&&judge(y,x+100000)&&judge(y+100000,x+200000)&&judge(y+200000,x)){
                link(x,y+100000);
                link(x+100000,y+200000);
                link(x+200000,y);
            }
            else if(flag==-1) flag = i%3;
        }
        else if(op==1){
            if(judge(x,y+100000)&&judge(x+100000,y+200000)&&judge(x+200000,y)&&judge(y,x+100000)&&judge(y+100000,x+200000)&&judge(y+200000,x)){
                link(x,y);
                link(x+100000,y+100000);
                link(x+200000,y+200000);
            }
            else if(flag==-1) flag = i%3;
        }
    }
    if(flag==0) flag = 3;
    printf("%d\n", flag);
    return 0;
}

K.爱吃瓜的伊卡洛斯(1)

题目链接
分析:这道题跟上一道题相似,但是不强调克制关系,只是两个对立集合,直接拆点,x就拆成x和x+100000。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,m;
int pre[N<<1];

int fin(int x){
    if(pre[x]==x) return x;
    else return pre[x] = fin(pre[x]);
}

void link(int x, int y){
    int dx = fin(x);
    int dy = fin(y);
    if(dx!=dy) pre[dy] = dx;
}

bool judge(int x, int y){
    int dx = fin(x);
    int dy = fin(y);
    if(dx==dy) return true;
    else return false;
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    cin>>n>>m;
    for(int i = 1; i <= n+100000; i++) pre[i] = i;
    string op;
    int x, y, yn;
    for(int i = 0; i < m; i++){
        cin>>op;
        if(op[0]=='A'){
            cin>>x>>y>>yn;
            if(yn==1){
                link(x,y);
                link(x+100000,y+100000);
            }
            else{
                link(x,y+100000);
                link(x+100000,y);
            }
        }
        else if(op[0]=='Q'){
            cin>>x>>y;
            if(judge(x,y)||judge(x+100000,y+100000)) cout<<"1"<<endl;
            else if(judge(x,y+100000)||judge(x+100000,y)) cout<<"2"<<endl;
            else cout<<"3"<<endl;
        }
    }
    return 0;
}

L.爱吃瓜的伊卡洛斯(2)

题目链接
分析:这道题跟上一道题的不同就是对立集合可以有无数个,难度瞬间上升好几个level,简单的并查集会把信息搞掉,所以这里就要用到启发式合并,启发式合并就是对于两棵树的合并,使合并之后树的深度变化尽量小(之类的),这道题,每一个点x维护一个集合,集合里面存储的是与x不同的点,如果x和y不同类则在x的set里加y,y的set里面加x。如果x和y是同类,就把x和y的集合合并,这里就用到启发式合并了,降低复杂度。使集合更高效,x和y就找他们的父节点进行合并,效率又快了一个level(实际上我不怎么会准确算复杂度)。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+5;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,m;
int pre[N];
set<int> st[N];

int fin(int x){
    return (pre[x]==x)?x:pre[x]=fin(pre[x]);
}

void link(int x, int y){
    int a = fin(x);
    int b = fin(y);
    if(a!=b){
        if(st[b].size()<st[a].size()) swap(a,b);  //启发式合并
        pre[a] = b;
        if(st[a].size()){
            for(set<int>::iterator it = st[a].begin(); it != st[a].end(); it++){
                st[b].insert(fin(*it));
            }
        }
    }
    return ;
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++) pre[i] = i;
    for(int i = 0; i < m; i++){
        string op; cin>>op;
        int x, y, z;
        if(op[0]=='A'){
            scanf("%d%d%d",&x,&y,&z);
            if(z==1) link(x,y);
            else{
                st[fin(x)].insert(fin(y));  //找父节点效率叫高
                st[fin(y)].insert(fin(x));
            }
        }
        else{
            scanf("%d%d",&x,&y);
            int dx = fin(x);
            int dy = fin(y);
            if(st[dx].find(dy)!=st[dx].end()||st[dy].find(dx)!=st[dy].end()) printf("2\n");
            else if(dx==dy) printf("1\n");
            else printf("3\n");
        }
    }
    return 0;
}

M.一道普通题1

题目链接

N - 一道普通的题2

题目链接

分析:这两道题分别是hzwer大神的分块入门3和分块入门5。
hzwer分块入门


O.帆宝RMQ

题目链接
分析:给出一组数,操作涉及区间修改,区间查询。(不会分析复杂度,直接给出思路)用分块,好似线段树暴力剪枝可以(还是数据太水?)?,分块的话常规根号n块大小,常规的对块进行修改,左右端点暴力,整块修改。对于每个查询,我们可以用vector存储块内的元素,对块内元素进行排序,找x的时候就对块内元素进行进行二分查找,如果这个区间右修改标记,就把x-atag[块],然后再进行查找,左右不完整的块就暴力查询,复杂度是xx(放弃治疗)。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 1e5+66;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

struct node{
    int v, id;
}gg[N];

bool cmp(const node &a, const node &b){
    if(a.v==b.v) return a.id<b.id;
    return a.v<b.v;
}

int n,q,blo;
int bl[N];
LL atag[N];
vector<node> ve[N];

void reset(int a){
    ve[a].clear();
    for(int i = (a-1)*blo+1; i <= min(a*blo,n); i++) {
        //gg[i].v+=atag[bl[a]];
        ve[a].push_back(gg[i]);
    }
    sort(ve[a].begin(),ve[a].end(),cmp);
}

void add(int l, int r, int x){
    for(int i = l; i <= min(bl[l]*blo,r); i++) gg[i].v+=x;
    reset(bl[l]);
    if(bl[l]!=bl[r]){
        for(int i = (bl[r]-1)*blo+1; i <= r; i++) gg[i].v+=x;
        reset(bl[r]);
    }
    for(int i = bl[l]+1; i <= bl[r]-1; i++){
        atag[i]+=x;
    }
}

int query(int x){
    int l = INF, r = -INF;
    for(int i = 1; i <= bl[n]; i++){
        node u;
        u.v = x-atag[i];
        u.id = 0;
        vector<node>::iterator it = lower_bound(ve[i].begin(),ve[i].end(),u,cmp);
        //vector::iterator it = ve[i].begin();
        if(it == ve[i].end()) continue;
        else{
            while(it != ve[i].end()){
                if((*it).v==u.v){
                    l = min(l,(*it).id);
                    r = max(r,(*it).id);
                }
                else break;
                it++;
            }
        }
    }
    if(l==INF||r==-INF) return -1;
    else return r-l;
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d%d",&n,&q);
    blo = sqrt(n);
    for(int i = 1; i <= n; i++){
        scanf("%d",&gg[i].v);
        gg[i].id = i;
    }
    for(int i = 1; i <= n; i++){
        bl[i] = (i-1)/blo+1;
        ve[bl[i]].push_back(gg[i]);
    }
    for(int i = 1; i <= bl[n]; i++) reset(i);
    for(int i = 1; i <= q; i++){
        int op; scanf("%d",&op);
        if(op==1){
            int l, r, x; scanf("%d%d%d",&l,&r,&x);
            add(l,r,x);
        }
        else{
            int x; scanf("%d",&x);
            printf("%d\n",query(x));
        }
    }
    return 0;
}

P.为什么你这么熟练啊

题目链接
分析:冬马是我的(雾),给出一组数,有若干个查询,每个查询查询两个区间,求这两个区间Σx=0∞(两个区间x出现次数之积),因为只有询问,所以我们很容易想到莫队(鼻青脸肿.jpg),在bi哥的纠正下才知道莫队是对块进行排序,之前一直以为是对询问的l,回归题目,对于每一个询问的一个区间,可以看成是(1r的x的数量)-(1l的x的数量),我们在移动l和r的指针的时候,例如r向右移动的时候,对于新加入的x,那么就对这个询问加上(1l)x的数量,为什么呢,看成这个时候(1~r)是增加了1个x,而l的x数量不变,所以总的就是增加了1*x个。而对于一整个询问,有如下神奇的公式
2018 UESTC Training For Data Structures_第1张图片

这张图是来自uestc群里面的题解PPT,讲的很好,很神奇。

即我们只要动态维护每一个区间lr位置的x值前缀就o98k了。

代码(感谢bi哥):

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair
#define eps 1e-6
//inline int lowbit(int x){return x&-x;}
//inline int the_mod(int &a,int &b){a=((a)%(b)+(b))%(b);}
const int N = 5e4+200;
const int mod = (int)1e9;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

int n,q,cnt = 0,blo,l1,l2,r1,r2;
LL v[N],res[N];
int cntl[N], cntr[N];
LL sum = 0;

struct node{
    int l,r,bl_id,id,x;
    bool operator < (const node &t) const{
        if(bl_id==t.bl_id) return r<t.r;
        return bl_id<t.bl_id;  //莫队精髓,对块进行排序
    }
}gg[N<<2];

void add_node(int l, int r, int id, int x){
    if(l>r) swap(l,r);
    if(l){
        gg[++cnt].l = l;
        gg[cnt].r = r;
        gg[cnt].id = id;
        gg[cnt].bl_id = l/blo;
        gg[cnt].x = x;
    }
}

void add_r(int pos){
    sum+=cntl[v[pos]];
    cntr[v[pos]]++;
}

void add_l(int pos){
    sum+=cntr[v[pos]];
    cntl[v[pos]]++;
}

void del_r(int pos){
    sum-=cntl[v[pos]];
    cntr[v[pos]]--;
}

void del_l(int pos){
    sum-=cntr[v[pos]];
    cntl[v[pos]]--;
}

int main()
{
#ifdef LOCAL
    freopen("test.txt", "r", stdin);
#endif // LOCAL
    scanf("%d",&n);
    blo = sqrt(n);
    for(int i = 1; i <= n; i++) scanf("%lld",&v[i]);
    scanf("%d",&q);
    for(int i = 1; i <= q; i++){
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        add_node(r1,r2,i,1);    //那个神奇的公式
        add_node(l1-1,l2-1,i,1);
        add_node(l2-1,r1,i,-1);
        add_node(l1-1,r2,i,-1);
    }
    sort(gg+1,gg+1+cnt);
    int L = 0, R = 0;
    for(int i = 1; i <= cnt; i++){
        while(R<gg[i].r){
            R++;
            add_r(R);
        }
        while(R>gg[i].r){
            del_r(R);
            R--;
        }
        while(L<gg[i].l){
            L++;
            add_l(L);
        }
        while(L>gg[i].l){
            del_l(L);
            L--;
        }
        res[gg[i].id]+=gg[i].x*sum;
    }
    for(int i = 1; i <= q; i++) printf("%lld\n",res[i]);
    return 0;
}

发现最后两题题目没有放出来,倒数第二题比较简单,线段树分块都可以做,最后一题是GDCPC2018的B题,有点难。

专题补完总结:学了分块,解锁新的并查集姿势(启发式),更熟悉了线段树,复习了单调队列/栈,STL真好用,因为各科作业较多,所以切题速度减慢,完成了课设,各科陆续结课,接下来时间会比较多,所以有更多的时间切专题了,当然还要保证不挂科,最后感谢康师傅的奖励。

你可能感兴趣的:(数据结构专题)