Week10-树形数据结构与应用

  • Post author:
  • Post category:其他




A : 小明上学

题目背景

小明是汉东省政法大学附属中学的一名学生,他每天都要骑自行车往返于家和学校。为了能尽可能充足地睡眠,他希望能够预计自己上学所需要的时间。他上学需要经过数段道路,相邻两段道路之间设有至多一盏红绿灯。

京州市的红绿灯是这样工作的:每盏红绿灯有红、黄、绿三盏灯和一个能够显示倒计时的显示牌。假设红绿灯被设定为红灯 rr 秒,黄灯 yy 秒,绿灯 gg 秒,那么从 00 时刻起,[0,r)[0,r) 秒内亮红灯,车辆不许通过;[r, r+g)[r,r+g) 秒内亮绿灯,车辆允许通过;[r+g, r+g+y)[r+g,r+g+y) 秒内亮黄灯,车辆不许通过,然后依次循环。倒计时的显示牌上显示的数字 l(l > 0)l(l>0)是指距离下一次信号灯变化的秒数。

问题描述

一次上学的路上,小明记录下了经过每段路的时间,和各个红绿灯在小明到达路口时的颜色和倒计时秒数。希望你帮忙计算此次小明上学所用的时间。

输入格式

输入的第一行包含空格分隔的三个正整数 rr、yy、gg,表示红绿灯的设置。这三个数均不超过 10^610 6。

输入的第二行包含一个正整数 n(n \leq 100)n(n≤100),表示小明总共经过的道路段数和看到的红绿灯数目。

接下来的 n 行,每行包含空格分隔的两个整数k、t。k=0 表示经过了一段道路,耗时 t 秒,此处 t 不超过 10^610 6 ;k=1、2、3 时,分别表示看到了一个红灯、黄灯、绿灯,且倒计时显示牌上显示的数字是 t,此处 t 分别不会超过 r、y、g。

输出格式

输出一个数字,表示此次小明上学所用的时间。

样例输入

30 3 30

8

0 10

1 5

0 11

2 2

0 6

0 3

3 10

0 3

样例输出

70

样例说明

小明先经过第一段道路,用时 1010 秒,然后等待 55 秒的红灯,再经过第二段道路,用时 1111 秒,然后等待 22 秒的黄灯和 3030 秒的红灯,再经过第三段、第四段道路,分别用时66、33秒,然后通过绿灯,再经过最后一段道路,用时 33 秒。共计 10 + 5 + 11 + 2 + 30 + 6 + 3 + 3=7010+5+11+2+30+6+3+3=70 秒。

评测用例规模与约定

测试点 1, 2 中不存在任何信号灯。

测试点 3, 4 中所有的信号灯在被观察时均为绿灯。

测试点 5, 6 中所有的信号灯在被观察时均为红灯。

测试点 7, 8 中所有的信号灯在被观察时均为黄灯。

测试点 9, 10 中将出现各种可能的情况。

#include<iostream>
using namespace std;
int main()

{int n;
int r,y,g;
cin>>r>>y>>g;
cin>>n;
int ans=0;
int k,t;
while(n--)
{
cin>>k>>t;
if(k==0)
	ans+=t;
else if(k==1)
	ans+=t;
else if(k==2)
	ans+=t+r;
}
cout<<ans<<endl;

} 

在这里插入图片描述



B : 小中大

题目背景

在数据分析中,最小值最大值以及中位数是常用的统计信息。

题目描述

老师给了你 nn 个整数组成的测量数据,不保证有序,可能存在重复的数据。请统计出这组测量数据中的最大值、中位数以及最小值,并按照从大到小的顺序输出这三个数。

输入格式

从标准输入读入数据。

第一行输入一个整数 nn,在第二行中存在 nn 个整数,表示测量数据,可能存在多个整数相等,整数与整数之间使用空格隔开。

输出格式

输出到标准输出。

包含一行,包括最大值、中位数以及最小值共三个数,并按照从大到小的顺序输出。数据与数据之间使用空格隔开。对于整数请直接输出整数,对于可能出现的分数,请输出四舍五入保留 11 位小数的结果。

样例1输入

3

-1 2 4

样例1输出

4 2 -1

样例1解释

44 为最大值,22 为中位数,-1−1 为最小值。

样例2输入

4

-2 -1 3 4

样例2输出

4 1 -2

样例2解释

44 为最大值,(-1+3)÷2=1(−1+3)÷2=1 为中位数,-2−2为最小值。

子任务

在这里插入图片描述


#include <vector>
#include <iostream>
#include <cstdio>
#include <algorithm>

int main()
{
    int n;
    std::vector<int> v;
    double cen;
    bool teil = false;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
    {
        int a;
        scanf("%d", &a);
        v.push_back(a);
    }
    std::sort(v.begin(), v.end());

    if (v.size() % 2 == 1)
    {
        cen = v[v.size() / 2];
    }
    else
    {
        cen = v[v.size() / 2] + v[v.size() / 2 - 1];
        cen /= 2;
        if (cen - (int)(cen) != 0)
        {
            teil = true;
        }
    }

    if (teil)
    {
        printf("%d %.1f %d\n", v.back(), cen, v.front());
    }
    else
    {
        printf("%d %.0f %d\n", v.back(), cen, v.front());
    }
    return 0;
}


在这里插入图片描述



C : 树状数组

题目描述

给定数列 a_1, a_2, \dots, a_na

1 ,a 2​ ,…,a n​

,你需要依次进行 qq 个操作,操作有两类:

1 i x:给定 i,xi,x,将 a_ia i 加上 xx;

2 l r:给定 l,rl,r,求 \sum_{i=l}^r a_i∑

i=lr a i​ 的值(换言之,求 a_l+a_{l+1}+\dots+a_ra l​ +a l+1​ +⋯+a r 的值)。

输入格式

第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1\le n,q\le 10^61≤n,q≤10 6 。

第二行 nn 个整数 a_1, a_2, \dots, a_na

1​ ,a 2 ,…,a n ,表示初始数列。保证 |a_i|\le 10^6∣a i ∣≤10 6 。

接下来 qq 行,每行一个操作,为以下两种之一:

1 i x:给定 i,xi,x,将 a[i]a[i] 加上 xx;

2 l r:给定 l,rl,r,求 \sum_{i=l}^r a_i∑

i=lr a i 的值。

保证 1\leq l\leq r\leq n,1≤l≤r≤n, |x|\le 10^6∣x∣≤10 6

输出格式

对于每个 2 l r 操作输出一行,每行有一个整数,表示所求的结果。

样例

输入样例

3 2

1 2 3

1 2 0

2 1 3

输出样例

6

数据范围

对于所有数据,1\le n,q\le 10^6,1≤n,q≤10 6 , |a_i|\le 10^6∣a i​ ∣≤10 6 , 1\le l\le r\le n,1≤l≤r≤n, |x|\le 10^6∣x∣≤10 6 。


#include <iostream>
#include <cstdio>

typedef long long un;

un tree[9999999];

un lowbit(un x)
{
    return x & (-x);
}

void update(un x, un diff, un size)
{
    for (un i = x; i < size; i += lowbit(i))
    {
        tree[i] += diff;
    }
}

un sumof(un x)
{
    un sum = 0;
    for (un i = x; i; i -= lowbit(i))
    {
        sum += tree[i];
    }
    return sum;
}

int main()
{
    un size;
    un ops;
    scanf("%lld %lld", &size, &ops);
    size++;
    for (un i = 1; i < size; i++)
    {
        un a;
        scanf("%lld", &a);
        update(i, a, size);
    }
    for (un i = 0; i < ops; i++)
    {
        int cmd;
        un a, b;
        scanf("%d %lld %lld", &cmd, &a, &b);
        if (cmd == 1)
        {
            update(a, b, size);
        }
        else
        {
            printf("%lld\n", sumof(b) - sumof(a - 1));
        }
    }
    return 0;
}



在这里插入图片描述



D : 排名

题目描述

sys 参加了一场 AI 算法大赛,大赛要求参赛者提供一个程序,后台的评测系统会使用相同的数据对所有提交程序进行评测,通过程序的运行时间与内存占用来衡量一个算法的好坏。

比赛的成绩计算方法为,给每一个程序进行打分,对于一个程序来说,该程序的得分为:运行时间与内存占用均小于等于该程序的程序的数量。

现在需要你来计算,成绩为 0,1,\dots n-10,1,…n−1 的程序的数量分别为多少。

输入格式

第一行一个整数 NN,表示程序的数目;

接下来 NN 行给出每个程序的运行时间与内存占用,用两个空格隔开的整数表示;

不会有两个程序的运行时间与内存占用均相同。

输出格式

nn 行,每行一个整数,分别是得分为 00,11,22,\dots…,n-1n−1 的程序的数目。

样例

输入

5

1 1

5 1

7 1

3 3

5 5

输出

1

2

1

1

0

数据范围

对于全部数据,1\le n\le 10^61≤n≤10

6

,0\le 运行时间,内存占用 \le 10^60≤运行时间,内存占用≤10

6

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
ll a[2000010],s[2000010];
ll lb(ll x)
{
	return x&(-x);
}
ll ask(ll x)
{
	ll res=0;
	for(int i=x;i>=1;i-=lb(i)) res+=s[i];
	return res; 
}
void upd(ll x,ll v)
{
	for(ll i=x;i<=2000010;i+=lb(i)) s[i]+=v;
}
struct node
{
	ll tim;ll use;
	node(int a,int b){tim=a,use=b;}
	bool operator < (const node &A)const{
        return tim<A.tim||(tim==A.tim&&use<A.use);
    }
};
int main()
{
	scanf("%lld",&n);
	vector<node> g;
	for(ll i=0;i<n;i++)
	{
		ll b,c;scanf("%lld%lld",&b,&c);
		c++,b++;
		node t(b,c);
		g.push_back(t);
	}
	sort(g.begin(),g.end());
	for(ll i=0;i<g.size();i++)
	{
        a[ask(g[i].use)]++;
        upd(g[i].use,1);
    }
    for(ll i=0;i<n;i++)
	{
	   cout<<a[i]<<'\n';	
	}
	return 0;
}

在这里插入图片描述



E : 火星饭店

题目描述

lzh 在火星开了一家饭店,为了吸引顾客,饭店会不定期在菜谱中加入新菜,每个菜都有一个美味度 l(0\le l < p)l(0≤l<p)。

饭店使用手机进行点餐,手机上展示的菜谱会按照更新的顺序逆序排列。由于不同顾客的手机屏幕分辨率大小不同,所以能够显示在第一屏的菜谱的数量也不同。根据调查发现,火星用户只会在第一屏的菜品中选择美味度最高的购买。

现在按照时间顺序输入 mm 个添加菜品或顾客点菜的操作,请输出每位点菜顾客所点的菜的美味度。

输入格式

第一行有两个正整数 m,pm,p,意义如题目描述;

接下来 mm 行,每一行表示一个操作。

如果该行的内容是 Q L,则表示有顾客进行了点菜,该顾客的手机屏幕可以显示 LL 个菜品;

如果是 A t,则表示加入了新的菜品,菜品的美味度是 (t+a)\bmod p(t+a)modp。

tt 是输入的参数

aa 是在这个添加操作之前最后一个点菜操作的答案(如果之前没有点菜操作,则 a = 0a=0)。

数据保证第一个操作一定是添加菜品。在顾客点菜时,L\gt 0L>0 且不超过当前已有菜品的数量。

输出格式

对于每一个点菜操作,输出一行。该行只有一个数,即当前屏幕中美味度最大的菜品的美味度。

样例

输入

10 100

A 97

Q 1

Q 1

A 17

Q 2

A 63

Q 1

Q 1

Q 3

A 99

输出

97

97

97

60

60

97

数据范围

对于全部数据,1\le m\le 2\times 10^5,1\le p\le 2\times 10^9,0\le t\lt p1≤m≤2×10

5

,1≤p≤2×10

9

,0≤t<p。

#include <iostream>
#include <cstdio>
#define MAX(a, b) (a > b ? a : b)
#define MIN(a, b) (a > b ? b : a)

typedef long long un;

un tree[9999999];
un tsize = 0;
un mr = 0;

void update(un pos, un value, un base = 1, un l = 1, un r = -1)
{
    if (r == -1) r = mr;
    if (l == r)
    {
        tree[base] += value;
        return;
    }
    un mid = (l + r) / 2;
    if (pos <= mid)
        update(pos, value, base * 2, l, mid);
    else
        update(pos, value, base * 2 + 1, mid + 1, r);
    tree[base] = MAX(tree[base * 2], tree[base * 2 + 1]);
}
un maxof(un pl, un pr, un base = 1, un l = 1, un r = -1)
{
    r = (r == -1 ? mr : r);
    if (pl == l && pr == r) return tree[base];
    un mid = (l + r) / 2;
    if (pr <= mid)
        return maxof(pl, pr, base * 2, l, mid);
    else if (pl > mid)
        return maxof(pl, pr, base * 2 + 1, mid + 1, r);
    else
    {
        return MAX(
                maxof(pl, mid, base * 2, l, mid),
                maxof(mid + 1, pr, base * 2 + 1, mid + 1, r));
    }
}

int main()
{
    un m, p;
    un qa = 0;
    tsize = 1;
    scanf("%lld %lld", &m, &p);
    mr = m;
    for (un i = 0; i < m; i++)
    {
        char c[4];
        scanf("%s", c);
        if (c[0] == 'Q')
        {
            un l;
            scanf("%lld", &l);
            qa = maxof(tsize - l, tsize - 1);
            printf("%lld\n", qa);

            /*for (int i = 0; i < mr * 4; i++)
            {
                printf("%lld ", tree[i]);
            }
            printf("\n");*/
        }
        else
        {
            un u;
            scanf("%lld", &u);
            u = (u + qa) % p;
            update(tsize, u);
            tsize++;
        }
    }
    return 0;
}


    

在这里插入图片描述



思路

A小明上学

每次判断当前的灯的颜色(k的值),如果k是0或3,则无需等待,通过道路,加上对应时间;如果k是1,则需要等待红灯时间,之后通过;如果k是2,则需等待黄灯、红灯时间,之后通过;累加时间输出即可。

B小中大

输入到数组中,之后对数组进行sort;

之后判断中位数的位置(是否需要两个相加除二),判断是否需要保留一位小数;

最后输出最后一个元素、中位数、第一个元素即可。

C树状数组

通过差分的思想;维护tree数组

对于给定 i,x,将 ai​ 加上 x操作:

将i到最后的元素都加上x

对于第二个操作 求l到r的和:

用前r项和减去前l-1项的和即可。

D 排名

构造结构体储存每个程序的运行时间、内存占用;

重载比较函数,用vector结构体数组存储输入,sort;

之后记录数目输出即可。

E

通过数组记录包括本项及之前所有的最大值;

即a[i]表示前i项中的最大值;

每次插入新菜则更新a[i];

顾客点菜则根据手机屏幕大小输出对应的最大值即可。



版权声明:本文为qq_45727515原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。