热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

HDU6521Party(SYSU校赛K题)(线段树)

题目链接题意:n个人排成一列,一开始他们互不认识,每次选[l,r]上的人开party,使他们互相认识,求出每次

题目链接

题意:n个人排成一列,一开始他们互不认识,每次选[l,r]上的人开party,使他们互相认识,求出每次party之后新互相认识的人的对数。

思路:把“互相认识”变成单向连边,只考虑左边的人对右边的贡献。对于每个人,他认识的人的区间必然是连续的,可以维护他认识的最右边的人R,这样更新操作相当于把[l,r]所有人的R值变成max(R,r),可以构造线段树维护每个区间中R的最小值mi,如果最小值大于等于R的话就不用更新了,直接退出,否则暴力修改每个点的值。

先上个假算法:

1 #include
2 using namespace std;
3 typedef long long ll;
4 const int N=5e5+10,inf=0x3f3f3f3f;
5 #define ls (u<<1)
6 #define rs (u<<1|1)
7 #define mid ((l&#43;r)>>1)
8 int mi[N<<2],n,m;
9 ll ans;
10 void pu(int u) {mi[u]&#61;min(mi[ls],mi[rs]);}
11 void build(int u&#61;1,int l&#61;1,int r&#61;n) {
12 if(l&#61;&#61;r) {mi[u]&#61;l; return;}
13 build(ls,l,mid),build(rs,mid&#43;1,r),pu(u);
14 }
15 void upd(int L,int R,int u&#61;1,int l&#61;1,int r&#61;n) {
16 if(l>R||r&#61;R)return;
17 if(l&#61;&#61;r) {ans&#43;&#61;R-mi[u],mi[u]&#61;R; return;}
18 upd(L,R,ls,l,mid),upd(L,R,rs,mid&#43;1,r),pu(u);
19 }
20 int main() {
21 while(scanf("%d%d",&n,&m)&#61;&#61;2) {
22 build();
23 while(m--) {
24 ans&#61;0;
25 int l,r;
26 scanf("%d%d",&l,&r);
27 upd(l,r);
28 printf("%lld\n",ans);
29 }
30 }
31 return 0;
32 }

View Code

这个算法本身是没有问题的&#xff0c;交上去也能AC&#xff0c;但会被一些极端的数据卡死&#xff0c;比如[1,1],[1,2],...,[1,n]这样的&#xff0c;会被卡成n^2&#xff0c;因此可以加一些优化。

由于每个人认识的最右边的人R的值是非递减的&#xff0c;即任意i>j&#xff0c;R[i]>&#61;R[j]&#xff0c;因此每次发生变化的区间必然是连续的&#xff0c;可以把单点修改换成区间修改&#xff0c;这样就不会被卡了。

1 #include
2 using namespace std;
3 typedef long long ll;
4 const int N&#61;5e5&#43;10,inf&#61;0x3f3f3f3f;
5 #define ls (u<<1)
6 #define rs (u<<1|1)
7 #define mid ((l&#43;r)>>1)
8 int mx[N<<2],mi[N<<2],lz[N<<2],n,m;
9 ll sum[N<<2],ans;
10 void pu(int u) {
11 mi[u]&#61;min(mi[ls],mi[rs]);
12 mx[u]&#61;max(mx[ls],mx[rs]);
13 sum[u]&#61;sum[ls]&#43;sum[rs];
14 }
15 void pd(int u,int l,int r) {
16 if(lz[u]) {
17 sum[ls]&#61;(ll)lz[u]*(mid-l&#43;1),sum[rs]&#61;(ll)lz[u]*(r-mid);
18 mi[ls]&#61;mi[rs]&#61;mx[ls]&#61;mx[rs]&#61;lz[ls]&#61;lz[rs]&#61;lz[u],lz[u]&#61;0;
19 }
20 }
21 void build(int u&#61;1,int l&#61;1,int r&#61;n) {
22 lz[u]&#61;0;
23 if(l&#61;&#61;r) {sum[u]&#61;mi[u]&#61;mx[u]&#61;l; return;}
24 build(ls,l,mid),build(rs,mid&#43;1,r),pu(u);
25 }
26 void upd(int L,int R,int u&#61;1,int l&#61;1,int r&#61;n) {
27 if(l>R||r&#61;R)return;
28 if(l>&#61;L&&r<&#61;R&&mx[u]<&#61;R) {sum[u]&#61;(ll)R*(r-l&#43;1),mi[u]&#61;mx[u]&#61;lz[u]&#61;R; return;}
29 pd(u,l,r);
30 upd(L,R,ls,l,mid),upd(L,R,rs,mid&#43;1,r),pu(u);
31 }
32 int main() {
33 while(scanf("%d%d",&n,&m)&#61;&#61;2) {
34 build(),ans&#61;sum[1];
35 while(m--) {
36 int l,r;
37 scanf("%d%d",&l,&r);
38 upd(l,r);
39 printf("%lld\n",sum[1]-ans);
40 ans&#61;sum[1];
41 }
42 }
43 return 0;
44 }

View Code

然后据说还有一种叫“吉司机线段树”的东西也能做&#xff1f;赶紧学了学(便乘)&#xff0c;感觉对于区间取max/min这类问题的处理强大的&#xff0c;普适性也比较高。

对于区间取max操作&#xff0c;其基本思想是维护区间和sum,区间最小值mi,区间次小值se以及区间最小值个数nmi。如果要对[l,r]上的所有数与x取max&#xff0c;那么分三种情况讨论即可&#xff1a;

1)若x<&#61;mi&#xff0c;则修改操作无效&#xff0c;退出

2)若mi

3)若x>&#61;se&#xff0c;则在左右区间递归进行下去

1 #include
2 using namespace std;
3 typedef long long ll;
4 const int N&#61;5e5&#43;10,inf&#61;0x3f3f3f3f;
5 #define ls (u<<1)
6 #define rs (u<<1|1)
7 #define mid ((l&#43;r)>>1)
8 int mi[N<<2],nmi[N<<2],se[N<<2],lz[N<<2],n,m;
9 ll sum[N<<2],ans;
10 void pu(int u) {
11 sum[u]&#61;sum[ls]&#43;sum[rs];
12 mi[u]&#61;min(mi[ls],mi[rs]),se[u]&#61;max(mi[ls],mi[rs]);
13 se[u]&#61;se[u]&#61;&#61;mi[u]?min(se[ls],se[rs]):min(se[u],min(se[ls],se[rs]));
14 nmi[u]&#61;(mi[ls]&#61;&#61;mi[u]?nmi[ls]:0)&#43;(mi[rs]&#61;&#61;mi[u]?nmi[rs]:0);
15 }
16 void change(int u,int x) {sum[u]&#43;&#61;(ll)nmi[u]*(x-mi[u]),mi[u]&#61;lz[u]&#61;x;}
17 void pd(int u) {
18 if(~lz[u]) {
19 if(mi[ls]<lz[u])change(ls,lz[u]);
20 if(mi[rs]<lz[u])change(rs,lz[u]);
21 lz[u]&#61;-1;
22 }
23 }
24 void build(int u&#61;1,int l&#61;1,int r&#61;n) {
25 lz[u]&#61;-1;
26 if(l&#61;&#61;r) {sum[u]&#61;mi[u]&#61;l,nmi[u]&#61;1,se[u]&#61;inf; return;}
27 build(ls,l,mid),build(rs,mid&#43;1,r),pu(u);
28 }
29 void upd(int L,int R,int x,int u&#61;1,int l&#61;1,int r&#61;n) {
30 if(l>R||rreturn;
31 if(l>&#61;L&&r<&#61;R&&xreturn;}
32 pd(u),upd(L,R,x,ls,l,mid),upd(L,R,x,rs,mid&#43;1,r),pu(u);
33 }
34 int main() {
35 while(scanf("%d%d",&n,&m)&#61;&#61;2) {
36 build(),ans&#61;sum[1];
37 while(m--) {
38 int l,r;
39 scanf("%d%d",&l,&r);
40 upd(l,r,r);
41 printf("%lld\n",sum[1]-ans);
42 ans&#61;sum[1];
43 }
44 }
45 return 0;
46 }

View Code

 

转:https://www.cnblogs.com/asdfsag/p/10773927.html



推荐阅读
  • pypy 真的能让 Python 比 C 还快么?
    作者:肖恩顿来源:游戏不存在最近“pypy为什么能让python比c还快”刷屏了,原文讲的内容偏理论,干货比较少。我们可以再深入一点点,了解pypy的真相。正式开始之前,多唠叨两句 ... [详细]
  • 线段树详解与实现
    本文详细介绍了线段树的基本概念及其在编程竞赛中的应用,并提供了一个具体的线段树实现代码示例。 ... [详细]
  • 本文探讨了如何高效地计算数组中和为2的幂的偶对数量,提供了从基础到优化的方法。 ... [详细]
  • UVa 1579 - 套娃问题
    本题主要涉及动态规划(DP)的应用,通过计算将前i个套娃合并成多个套娃组所需的最小操作次数来解决问题。具体来说,f(i) 表示前i个套娃合并成多个套娃组所需的操作次数,其计算公式为 f(i) = min(f(j) + dp(j+1, i))。 ... [详细]
  • 题目编号:2049 [SDOI2008]Cave Exploration。题目描述了一种动态图操作场景,涉及三种基本操作:断开两个节点间的连接(destroy(a,b))、建立两个节点间的连接(connect(a,b))以及查询两节点是否连通(query(a,b))。所有操作均确保图中无环存在。 ... [详细]
  • 本文详细介绍了如何在循环双链表的指定位置插入新元素的方法,包括必要的步骤和代码示例。 ... [详细]
  • 编译原理中的语法分析方法探讨
    本文探讨了在编译原理课程中遇到的复杂文法问题,特别是当使用SLR(1)文法时遇到的多重规约与移进冲突。文章讨论了可能的解决策略,包括递归下降解析、运算符优先级解析等,并提供了相关示例。 ... [详细]
  • POJ2263是一个经典的图论问题,涉及寻找从起点到终点的最大载重路径。本文将详细介绍该问题的背景、解题思路及代码实现。 ... [详细]
  • Spring Boot使用AJAX从数据库读取数据异步刷新前端表格
      近期项目需要是实现一个通过筛选选取所需数据刷新表格的功能,因为表格只占页面的一小部分,不希望整个也页面都随之刷新,所以首先想到了使用AJAX来实现。  以下介绍解决方法(请忽视 ... [详细]
  • 本题涉及一个长度为n的序列{ai},代表一系列树木的美学价值。任务是处理m个查询,每个查询提供三个参数l、r和P,目标是在所有满足l < l' ... [详细]
  • 题目描述:计算从起点到终点的最小能量消耗。如果下一个单元格的风向与当前单元格相同,则消耗为0,否则为1。共有8个可能的方向。 ... [详细]
  • RTThread线程间通信
    线程中通信在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取& ... [详细]
  • php三角形面积,335宝石大全
    php三角形面积,335宝石大全 ... [详细]
  • c#  项目文件,C#viual studio使用方法
    一、项目文件1)Properties节点下主要存放的是当前程序集相关的信息,如版本号、标题等。双击”Properties“,打开如下项目属 ... [详细]
  • 本文详细介绍了Sleep函数的基本概念、使用方法及其背后的实现原理。适合对Sleep函数的使用和实现感兴趣的开发者阅读。通过本文,您将了解如何在不同操作系统中使用Sleep函数,以及其在多线程编程中的重要性。 ... [详细]
author-avatar
烦恼的天伦之乐_456
这个家伙很懒,什么也没留下!
RankList | 热门文章