YYHS-猜数字(并查集/线段树维护)

题目描述

LYK在玩猜数字游戏。

总共有n个互不相同的正整数,LYK每次猜一段区间的最小值。形如[li,ri]这段区间的数字的最小值一定等于xi。

我们总能构造出一种方案使得LYK满意。直到…… LYK自己猜的就是矛盾的!

例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,这显然就是矛盾的。

你需要告诉LYK,它第几次猜数字开始就已经矛盾了。

输入

第一行两个数n和T,表示有n个数字,LYK猜了T次。
    接下来T行,每行三个数分别表示li,ri和xi。

输出

输出一个数表示第几次开始出现矛盾,如果一直没出现矛盾输出T+1。

样例输入

20 4
1 10 7
5 19 7
3 12 8
1 20 1

样例输出

3

提示

数据范围

对于50%的数据n<=8,T<=10。

对于80%的数据n<=1000,T<=1000。

对于100%的数据1<=n,T<=1000000,1<=li<=ri<=n,1<=xi<=n(但并不保证一开始的所有数都是1~n的)

Hint

建议使用读入优化

inline int read()

{

int x = 0, f = 1;

char ch = getchar();

for(; !isdigit(ch); ch = getchar()) if(ch == ‘-‘) f = -1;

for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - ‘0‘;

return x * f;

}

题解

这道题我们先考虑矛盾的情况

我们不难发现有以下两种情况是矛盾的

1.当一个区间覆盖了另一个区间且大的区间的x值比另一个区间的x值小的时候是矛盾的

2.当两个区间的x值相同时,如果这两个区间没有交集,这也是矛盾的

知道了矛盾的情况后

我们可以二分矛盾的句子的位置

将前k个句子按x值从大到小排个序,然后我们枚举,判断当前区间的x值和前一个区间的x值是否相同

如果相同,就判断一下有没有交集

如果不相同,我们可以维护一个线段树,将交集的区间覆盖为1,查询并集的区间是否被覆盖为1,当然我们也可以用并查集来维护,我是用并查集来做的,但还是感觉线段树应该好懂一些(虽然代码长了些)

 1 #include<bits/stdc++.h>
 2 #define N 1000005
 3 using namespace std;
 4 int n,T,cnt;
 5 int fa[N];
 6 struct node{
 7     int l,r,x;
 8 }a[N],b[N];
 9 bool cmp(node x,node y){ return x.x>y.x; }
10 int find(int x){ if (x!=fa[x]) fa[x]=find(fa[x]); return fa[x]; }
11 bool check(int x){
12     int f1,f2;
13     for (int i=1;i<=n+1;i++) fa[i]=i;
14     for (int i=1;i<=x;i++) b[i]=a[i];
15     sort(b+1,b+1+x,cmp);
16     int lmin=b[1].l,lmax=b[1].l,rmin=b[1].r,rmax=b[1].r;
17     for (int i=2;i<=x;i++){
18         if (b[i].x<b[i-1].x){
19             f1=find(lmax);
20             if (f1>rmin) return true;//判断是否有大于b[i].x的区间覆盖了
21             f2=find(rmax+1);
22             for (int j=find(lmin);j<=rmax;j++){
23                 f1=find(j);
24                 fa[f1]=f2;
25             }
26             lmin=lmax=b[i].l;
27             rmin=rmax=b[i].r;
28         } else{
29             lmin=min(lmin,b[i].l);
30             lmax=max(lmax,b[i].l);
31             rmin=min(rmin,b[i].r);
32             rmax=max(rmax,b[i].r);
33             if (lmax>rmin) return true;//判断是否有交集
34         }
35     }
36     f1=find(lmax);
37     if (f1>rmin) return true;
38     return false;
39 }
40 int main(){
41     scanf("%d%d",&n,&T);
42     for (int i=1;i<=T;i++)
43         scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].x);
44     int l=1,r=T;
45     int ans=T+1;
46     while (l<=r){
47         int mid=(l+r)>>1;
48         if (check(mid)){
49             ans=mid;
50             r=mid-1;
51         } else l=mid+1;
52     }
53     printf("%d\n",ans);
54     return 0;
55 }

时间: 2024-09-30 00:13:01

YYHS-猜数字(并查集/线段树维护)的相关文章

UVA1455 - Kingdom(并查集 + 线段树)

UVA1455 - Kingdom(并查集 + 线段树) 题目链接 题目大意:一个平面内,给你n个整数点,两种类型的操作:road x y 把city x 和city y连接起来,line fnum (浮点数小数点一定是0.5) 查询y = fnum这条直线穿过了多少个州和city.州指的是连通的城市. 解题思路:用并查集记录城市之间是否连通,还有每一个州的y的上下界.建立坐标y的线段树,然后每次运行road操作的时候,对范围内的y坐标进行更新:更新须要分三种情况:两个州是相离,还是相交,还是包

uva 1455 - Kingdom(并查集+线段树)

题目链接:uva 1455 - Kingdom 题目大意:平面上又n个城市,初始时城市之间没有任何双向道路相连,要求一次执行指令. road A B :在城市A和城市B之间连接一条双向道路 line C:询问一条y=C的水平线上穿过多少州和这些州总共有多少城市. 一个联通分量算一个州,C保证为小数部分为0.5的实数. 解题思路:线段树维护每个位置上州和城市的个数,并查集维护哪些城市属于同一个州,并且要记录这些州上下范围.每次新建一条道路,要相应根据两个州的y坐标范围对线段树进行维护. #incl

bzoj 2733 永无乡 - 并查集 - 线段树

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输

bzoj2733 [ HNOI2012 ] -- 并查集+线段树合并

用并查集记录每个联通块的根节点,每个联通块建一棵线段树,合并时合并线段树就可以了. 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define N 100010 6 struct node{ 7 int l,r,x; 8 }c[N*20]; 9 int i,j,k,n,m,l,r,a[N],x,y,Rt[N],Num,b[N],f[N],

[BZOJ2733] [HNOI2012]永无乡(并查集 + 线段树合并)

传送门 一看到第k大就肯定要想到什么权值线段树,主席树,平衡树之类的 然后就简单了 用并查集判断连通,每个节点建立一颗权值线段树,连通的时候直接合并即可 查询时再二分递归地查找 时间复杂度好像不是很稳定...但hzwer都用这种方法水过.. 正解好像是平衡树+启发式合并,以后学TT #include <cstdio> #include <iostream> #define N 100001 int n, m, q, cnt; int a[N], f[N], sum[N * 20],

并查集 + 线段树 LA 4730 Kingdom

题目传送门 题意:训练指南P248 分析:第一个操作可以用并查集实现,保存某集合的最小高度和最大高度以及城市个数.运用线段树成端更新来统计一个区间高度的个数,此时高度需要离散化.这题两种数据结构一起使用,联系紧密. #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; const int M = 3 * N; const int INF = 0x3f3f3f3f; struct Point { int x, y

uvalive 4730王国kingdom(并查集+线段树)

 题意:有T组测试数据,每组数据的N表示有N个城市,接下来的N行里每行给出每个城市的坐标(0<=x,y<=1000000),然后有M(1<M<200000)个操作,操作有两类,(1)"road A B",表示将城市A和城市B通过一条道路连接,如果A和B原来属于不同的城市群,经过这个操作,A和B就在一个城市群里了,保证每条道路不会和其他道路相交(除了端点A和B).(2)"line C",表示查询当穿过y=C的直线,有多少个城市群.这几个城市

BZOJ 3319 黑白树 并查集+线段树

这这这这这这什么毒瘤题!!!!!!!!!!!!!!!!!!!!!!!!!!!! 卡LCT(优秀的LCT由于是均摊本身就带着2,3的常数在,而且这道题对于LCT标记十分难维护,又得乘上4,5然后就炸了),卡树剖,卡正解,但是暴力能A!!!!!!!!!!!!!!!!!!!!!! 所谓正解就是线段树为护dfs序+并查集删点去重,这东西在每个点一秒的时候都过不了Po姐都虚. 但是我在网上看到一个大佬有一个神思路A掉了 下面是我改过之后的的TLE程序 #include<cstdio> #include&

Codeforces Gym 101194G Pandaria (2016 ACM-ICPC EC-Final G题, 并查集 + 线段树合并)

题目链接  2016 ACM-ICPC EC-Final Problem G 题意  给定一个无向图.每个点有一种颜色. 现在给定$q$个询问,每次询问$x$和$w$,求所有能通过边权值不超过w的边走到$x$的点的集合中,哪一种颜色的点出现的次数最多. 次数相同时输出编号最小的那个颜色.强制在线. 求哪种颜色可以用线段树合并搞定. 关键是这个强制在线. 当每次询问的时候,我们先要求出最小生成树在哪个时刻恰好把边权值不超过$w$的边都用并查集合并了. 在做最小生成树的时候每合并两个节点,另外开一个