【UESTC】2015 UESTC Training for Data Structures

题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=102504#overview

这套题放了很久一直没写,直到我终于发现,现在已经不得不练数据结构的时候了,于是把这套题翻了出来开始写,嘛……总之感谢UESTC的题目先。估计全部写完大概要一周的时间,最近又因为期末考试没什么时间,大概会拖很久吧……


【UESTC1059】秋实大哥与小朋友

题意为对一个连续区间[1,n],有两种操作,将某一段区间的值增加v,或者查询某一个点的值。题目中给定的n很大,需要使用离散化,修改和查询使用线段树和树状数组完成均可,使用树状数组时,是“修改区间,查询点”的一种使用方式,具体操作为,在增加的时候,执行add(l,v)和add(r+1,-v),使[1,l-1]和[r+1,n]的值不变,只改变[l,r]区间的值。查询的时候执行query(x),统计x节点的值,类似于统计覆盖线段的一种做法。除此之外我还查到另一种解法……等我看懂了把代码贴上来。


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef long long LL;
struct Act{
    int k,l,r;
    LL v;
};
map<int,int> dir;
vector<int> sav;
Act a[100100];
LL t[300100];
int n,m,cnt;
int lowbit(int k) {
    return k&(-k);
}
void add(int k,LL x) {
    while (k <= cnt) {
        t[k] += x;
        k += lowbit(k);
    }
}
LL query(int k) {
    LL ans = 0;
    while (k > 0) {
        ans += t[k];
        k -= lowbit(k);
    }
    return ans;
}
int main() {
    while (scanf("%d%d",&n,&m) != EOF) {
        dir.clear();
        memset(t,0,sizeof(t));
        sav.clear();sav.push_back(-1);
        for (int i = 1;i <= m; i++) {
            scanf("%d",&a[i].k);
            if (!a[i].k) {
                scanf("%d%d%lld",&a[i].l,&a[i].r,&a[i].v);
                sav.push_back(a[i].l);sav.push_back(a[i].r);
            } else {
                scanf("%d",&a[i].l);
                sav.push_back(a[i].l);
            }
        }
        sort(sav.begin(),sav.end());cnt = 0;
        for (int i = 1;i < sav.size();i++)
            if (sav[i] != sav[i-1]) dir[sav[i]] = ++cnt;
        for (int i = 1;i <= m; i++) {
            if (!a[i].k) {
                //cout << dir[a[i].l] << ":" << dir[a[i].r] << endl;
                add(dir[a[i].l],a[i].v);
                add(dir[a[i].r]+1,-a[i].v);
            } else {
                //cout << dir[a[i].v] << endl;
                printf("%lld\n",query(dir[a[i].l]));
            }
        }
    }
	return 0;
}

【UESTC1057】秋实大哥与花


对于给定区间完成区间修改和区间查询,线段树的基本应用。


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef long long LL;
LL t[400100],lazy[400100];
void down(int l,int r,int k) {
    int mid = (l + r) >> 1;
    lazy[k<<1] += lazy[k];t[k<<1] += (mid-l+1)*lazy[k];
    lazy[(k<<1)|1] += lazy[k];t[(k<<1)|1] += (r-mid)*lazy[k];
    lazy[k] = 0;
}
void build(int l,int r,int k) {
    if (l == r) {
        lazy[k] = 0;
        scanf("%lld",&t[k]);
        return;
    }
    int mid = (l + r) >> 1;
    build(l,mid,k<<1);
    build(mid+1,r,(k<<1)|1);
    t[k] = t[k<<1] + t[(k<<1)|1];
}
void updata(int l,int r,int k,int x,int y,LL val) {
    //printf("%d %d %d\n",l,r,k);
    if (x <= l && r <= y) {
        lazy[k] += val;
        t[k] += (r-l+1)*val;
        return;
    }
    if (lazy[k]) down(l,r,k);
    int mid = (l+r) >> 1;
    if (x <= mid) updata(l,mid,k<<1,x,y,val);
    if (y > mid) updata(mid+1,r,(k<<1)|1,x,y,val);
    t[k] = t[k<<1] + t[(k<<1)|1];
}
LL query(int l,int r,int k,int x,int y) {
    //printf("%d %d %d\n",l,r,t[k]);
    if (x <= l && r <= y) {
        return t[k];
    }
    if (lazy[k]) down(l,r,k);
    int mid = (l + r) >> 1;LL s1 = 0,s2 = 0;
    if (x <= mid) s1 = query(l,mid,k<<1,x,y);
    if (y > mid) s2 = query(mid+1,r,(k<<1)|1,x,y);
    return s1+s2;
}
int main() {
    int n,m;
    int l,r;LL v;
    while (scanf("%d",&n) != EOF) {
        build(1,n,1);
        scanf("%d",&m);
        for (int i = 1;i <= m; i++) {
            scanf("%d%d%lld",&l,&r,&v);
            updata(1,n,1,l,r,v);
            printf("%lld\n",query(1,n,1,l,r));
        }
    }
	return 0;
}



【UESTC1060】秋实大哥与快餐店

给出一些数a[1..n],有两种操作,再给出一个数a[i],或是对一个数k,求出给定的数中与k异或值最大的数。字典树的应用,可以将给出的数a[i]转换为二进制串后,从高位到低位进行建树(为保证位数相同需要补零)。对于给定的数k,在走到第i层节点时,尽量寻找与k第i位值不同的节点,这样找到的数就是与k异或值最大的数。


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
struct node{
    int k,next[2];
};
vector<node> a;
int cnt,root;
int newnode() {
    node n;
    n.k = n.next[0] = n.next[1] = 0;
    a.push_back(n);
    //cout << cnt << endl;
    return a.size()-1;
}
void ins(int x) {
    int p = root,k;
    for (int i = 21;i >= 0; i--) {
        k = x&(1 << i)?1:0;

        //cout << k << endl;
        if (!a[p].next[k]) {
            int sav = newnode();
            a[p].next[k] = sav;
            //cout << a[p].next[k] << endl;
        }
        //printf("%d %d %d\n",p,a[p].next[0],a[p].next[1]);
        p = a[p].next[k];
    }
}
int query(int x) {
    int p = root,k,ans = 0;
    for (int i = 21;i >= 0; i--) {
        k = x&(1 << i)?0:1;
        if (!a[p].next[k]) k = 1 - k;
        if (k) ans += (1 << i);
        p = a[p].next[k];
    }
    return ans;
}
int main() {
    int n,m,x,y;
    while (scanf("%d",&n) != EOF) {
        a.clear();cnt = 0;
        root = newnode();
        for (int i = 1;i <= n; i++) {
            scanf("%d",&x);
            ins(x);
        }
        scanf("%d",&m);
        while (m--) {
            scanf("%d%d",&x,&y);
            if (!x) ins(y);
            else printf("%d\n",query(y));
        }
    }
	return 0;
}

【UESTC1061】秋实大哥与战争


问题的实质在于寻找士兵k左右最近的死亡士兵,设左边最近的死亡士兵为l,右边的为r,则答案为r-l-1,也就是说,我们要完成(1)添加一个数(2)删除一个数(3)寻找与给定的数k最近的两个数,而集合set恰好能满足我们的需要(set是有序的,可以使用lower_bound进行查询),我们使用两个集合,一个集合存储正的坐标用来寻找l,另一个存储负坐标用于寻找r,在最开始需要把n+1和0添加到集合中。


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
set<int> Sinc,Sdec;
int main() {
    int n,m;
    int act,k;
    while (scanf("%d%d",&n,&m) != EOF) {
        Sinc.clear();Sdec.clear();
        Sinc.insert(n+1);
        Sdec.insert(0);
        while (m--) {
            scanf("%d%d",&act,&k);
            if (act == 0) {Sinc.insert(k);Sdec.insert(-k);}
            if (act == 1) {Sinc.erase(k);Sdec.erase(-k);}
            if (act == 2) {
                set<int>::iterator st = Sinc.lower_bound(k);
                set<int>::iterator ed = Sdec.lower_bound(-k);
                if (*st == k) puts("0");
                else printf("%d\n",(*st)+(*ed)-1);
            }
        }
    }
	return 0;
}

【UESTC1063】秋实大哥与妹纸


寻找a1...an的中位数,即第k大数问题,用堆或快排的折半查找均可。


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef unsigned int LL;
LL a[250000],m;
void add(int k) {
    LL val = a[k];
    while (k > 1) {
        if (val > a[k >> 1]) {
            a[k] = a[k >> 1];
            k >>= 1;
        } else break;
    }
    a[k] = val;
}
void del() {
    LL val = a[m--],k = 1;
    while (k < m) {
        if ((k << 1) > m) break;
        if ((k << 1) == m || a[(k << 1)|1] < a[k << 1]) {
            if (a[(k << 1)] > val) {
                a[k] = a[(k << 1)];
                k = (k << 1);
            } else break;
        } else {
            if (a[(k << 1)|1] > val) {
                a[k] = a[(k << 1)|1];
                k = (k << 1)|1;
            } else break;
        }
    }
    a[k] = val;
}
int main() {
    int n,k,mid;
    scanf("%d",&n);mid = n/2 + 1;
    for (int i = 1;i <= mid; i++) {
        scanf("%ud",&a[++m]);
        add(m);
    }
    for (int i = mid+1;i <= n; i++) {
        scanf("%ud",&a[++m]);
        add(mid+1);
        del();
    }
    //for (int i = 1;i <= mid; i++) cout << a[i] << " ";cout << endl;
    if ((n&1) == 0) {
        int k = a[1];del();
        printf("%.1lf\n",(k+a[1])/2.0);
    } else {
        printf("%.1lf\n",(double)a[1]);
    }
	return 0;
}



【UESTC1069】秋实大哥去打工


寻找最大的连续矩形,明显的,最大矩形的高度一定与某个给定的矩形高度相等,所以对于每一个给定的矩形,以ai为高度的连续矩形可以向左右延伸,直到找到aj<ai为止。使用单调栈存储递增的矩形序列,入栈时确定了连续矩形的左边界,出栈时确定了连续矩形的右边界。


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
struct mat{
    int h,w;
};
mat Q[200100];
int main() {
    int n;
    scanf("%d",&n);
    mat m;
    int p = 0,ans = 0;
    for (int i = 1;i <= n; i++) {
        scanf("%d%d",&m.w,&m.h);
        int sumw = 0;
        while (p > 0 && m.h <= Q[p].h) {
            ans = max(ans,Q[p].h*(Q[p].w+sumw));
            sumw += Q[p--].w;
        }
        m.w += sumw;
        Q[++p] = m;
    }
    int sumw = 0;
    while (p > 0) {
        ans = max(ans,Q[p].h*(Q[p].w + sumw));
        sumw += Q[p--].w;
    }
    printf("%d\n",ans);
	return 0;
}


【UESTC1070】秋实大哥打游戏


有权并查集,添加一个dis[i]数组表示i节点到f[i]节点的距离,在find中更新f[i]和dis[i]数组,注意读入有坑……最后一行没换行。


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
const int maxn = 1e5 + 100;
int f[maxn],dis[maxn];
stack<int> S;
int find(int k) {
    while (f[k] != k) {
        S.push(k);
        k = f[k];
    }
    int sum = 0;
    while (!S.empty()) {
        int p = S.top();S.pop();
        sum += dis[p];dis[p] = sum;
        f[p] = k;
    }
    return k;
}
int main() {
    int n,x,y,fy;char ch;
    scanf("%d",&n);
    for (int i = 1;i <= n; i++) {f[i] = i;dis[i] = 0;}
    while (scanf(" %c",&ch) != EOF && ch != 'O') {
        if (ch == 'E') {
            scanf("%d",&x);
            find(x);
            printf("%d\n",dis[x]);
        }
        if (ch == 'I') {
            scanf("%d%d",&x,&y);
            x = find(x);fy = find(y);
            if (x != fy) {f[x] = y;dis[x] = abs(x-y)%1000;}
        }
    }
    //for (int i = 1;i <= n; i++) printf("%d %d\n",f[i],dis[i]);
	return 0;
}


【UESTC1073】秋实大哥与线段树


没什么可说的……


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 100;
int n,m;
LL a[maxn],t[maxn];
void add(int k,int val) {
    for (int i = k;i <= n; i += i&(-i)) t[i] += val;
}
LL query(int k) {
    LL ans = 0;
    for (int i = k;i > 0; i -= i&(-i)) ans += t[i];
    return ans;
}
int main() {
    int l,r,k;
    scanf("%d",&n);
    for (int i = 1;i <= n; i++) {
        scanf("%d",&a[i]);
        add(i,a[i]);
    }
    scanf("%d",&m);
    for (int i = 1;i <= m; i++) {
        scanf("%d%d%d",&k,&l,&r);
        if (k == 1) {add(l,r-a[l]);a[l] = r;}
        if (k == 2) printf("%lld\n",query(r)-query(l-1));
    }
	return 0;
}


【UESTC1074】秋实大哥搞算数


四则表达式计算,栈的基本应用,正统做法是为运算符规定优先级……这里因为没有括号就偷懒了。减法运算转化为加上负数可以使运算简便一些。


#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<sstream>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef long long LL;
string st;
stack<LL> num;
stack<char> op;
void fig() {
    LL k2 = num.top();num.pop();
    LL k1 = num.top();num.pop();
    char act = op.top();op.pop();
    //cout << k1 << act << k2 << endl;
    if (act == '+') k1 += k2;
    if (act == '-') k1 -= k2;
    if (act == '*') k1 *= k2;
    if (act == '/') k1 /= k2;
    num.push(k1);
}
int main() {
    int t;
    scanf("%d",&t);
    while (t--) {
        num.push(0);
        LL k = 0;int sgn = 1;
        cin >> st;
        for (int i = 0;i < st.size(); i++) {
            if (st[i] >= '0' && st[i] <= '9') k = k*10 + (st[i] - '0')*sgn;
            else {
                //cout << k << endl;
                num.push(k);k = 0;
                while (!op.empty() && (op.top() == '*' || op.top() == '/')) fig();
                if (st[i] == '-') {
                    st[i] = '+';
                    sgn = -1;
                } else sgn = 1;
                op.push(st[i]);
            }
        }
        num.push(k);k = 0;
        while (!op.empty()) fig();
        if (!num.empty()) {
            printf("%lld\n",num.top());
            while (!num.empty()) num.pop();
        }
    }
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值