noip模拟赛 猜数字
题目描述
LYK在玩猜数字游戏。
总共有n个互不相同的正整数,LYK每次猜一段区间的最小值。形如[li,ri]这段区间的数字的最小值一定等于xi。
我们总能构造出一种方案使得LYK满意。直到…… LYK自己猜的就是矛盾的!
例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,这显然就是矛盾的。
你需要告诉LYK,它第几次猜数字开始就已经矛盾了。
输入格式(number.in)
第一行两个数n和T,表示有n个数字,LYK猜了T次。
接下来T行,每行三个数分别表示li,ri和xi。
输出格式(number.out)
输出一个数表示第几次开始出现矛盾,如果一直没出现矛盾输出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;
}
分析:有点难度的一道题.
总是感觉题意自相矛盾,打的50分暴力只得了40分?最后看数据n=8, 1 8 2这种猜测竟然合法!题目中明明说了每个数字都是正整数并且互不相同,虽然最后说了并不保证一开始的所有数都是1~n的,但我并不明白这前后是啥意思.
40分暴力根据题目意思来就好了,相当于是一个1~n的全排列,每次判断这个排列到几个猜测就不合法了,记录下最大的就是答案.生成全排列可以用STL中的next_permutation.
100分的做法比较神,由于每个区间只能知道它的最小值,并不知道它在哪,其它的值是什么.现在要确定的就是怎么样才能判断一次猜测是不合法的.从题目给的样例可以看出,如果一个x较小的区间被x较大的区间给完全覆盖住了,那么这就是不合法的.根据这种判断方法,可以先对所有区间按照x从大到小排序,看这个区间有没有被之前的区间给覆盖.这就有一个问题:我不知道这个区间是第几次询问,那么二分第k次出现询问,把第一个到第k个区间排序就行了.
进行覆盖操作肯定不能一个一个暴力覆盖,可以用线段树来维护,但是会T掉两个点,正解是并查集,维护每个点能延伸到最右边的哪个点,判断是否大于当前区间的右端点.对于许多个x值相等的区间,可以证明的是最小值一定在它们的交集中,因为每个数都是正整数并且互不相等,所以只需要关注是否交集被完全覆盖了即可.如果x值一定的区间只有一个就判断这个区间是否被完整覆盖即可.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n, T, ans, fa[1000010]; struct node { int l, r, x; }p[1000010],e[1000010]; bool cmp(node a, node b) { return a.x > b.x; } int find(int x) { if (x == fa[x]) return x; return fa[x] = find(fa[x]); } bool check(int k) { for (int i = 1; i <= n + 1; i++) fa[i] = i; for (int i = 1; i <= k; i++) p[i] = e[i]; sort(p + 1, p + 1 + k,cmp); int lmax = p[1].l, lmin = p[1].l, rmax = p[1].r, rmin = p[1].r; for (int i = 2; i <= k; i++) { if (p[i].x < p[i - 1].x) { if (find(lmax) > rmin) return true; for (int j = find(lmin); j <= rmax; j++) fa[find(j)] = find(rmax + 1); lmin = lmax = p[i].l; rmin = rmax = p[i].r; } else { lmin = min(lmin, p[i].l); lmax = max(lmax, p[i].l); rmin = min(rmin, p[i].r); rmax = max(rmax, p[i].r); if (find(lmax) > rmin) return true; } } if (find(lmax) > rmin) return true; return false; } int main() { scanf("%d%d", &n, &T); for (int i = 1; i <= T; i++) scanf("%d%d%d", &e[i].l, &e[i].r, &e[i].x); int l = 1, r = T; ans = T + 1; while (l <= r) { int mid = (l + r) >> 1; if (check(mid)) { ans = mid; r = mid - 1; } else l = mid + 1; } printf("%d\n", ans); return 0; }