并查集的神奇用法:[JSOI2008]最大数 https://www.luogu.com.cn/problem/P1198
Description
现在请求你维护一个数列, 要求提供以下两种操作:
1, 查询操作.
语法: Q L
功能: 查询当前数列中末尾 L 个数中的最大的数, 并输出这个数的值.
限制: LL 不超过当前数列的长度.(L> 0)(L>0)
2, 插入操作.
语法: A n
功能: 将 nn 加上 tt, 其中 tt 是最近一次查询操作的答案(如果还未执行过查询操作, 则 t=0t=0), 并将所得结果对一个固定的常数 DD 取模, 将所得答案插入到数列的末尾.
限制: nn 是整数 (可能为负数) 并且在长整范围内.
注意: 初始时数列是空的, 没有一个数.
输入格式
第一行两个整数, MM 和 DD, 其中 MM 表示操作的个数(M \le 200,000)(M≤200,000),DD 如上文中所述, 满足(0<D<2,000,000,000)(0<D<2,000,000,000)
接下来的 MM 行, 每行一个字符串, 描述一个具体的操作. 语法如上文所述.
输出格式
对于每一个查询操作, 你应该按照顺序依次输出结果, 每个结果占一行.
Solution
首先我们来观察这道题的性质: 所有的查询都是从最后开始的
这意味着甚么?
我们只用对每个数维护它到最后一个数之间的数的最大值就可以了
但会不断在末端加入数怎么办? 并查集上场了
每加入一个数时, 像单调栈一样把从它到它前面第一个比它大的数之前的一个数之间的所有数的最深父亲设为它;
这样, 就可以在 logn 的时间内处理问题
上代码
- Code
- #include <cstdio>
- #include <cstdlib>
- using namespace std;
- const int N=200010;
- struct node
- {
- long long x;
- int y;
- }a[N];
- int m,tot,cnt,f[N];
- long long d,t,x,num[N];
- char ch[3];
- int find(int x)
- {
- return f[x]==x?x:f[x]=find(f[x]);
- }
- int main()
- {
- scanf("%lld%lld",&m,&d);
- for(int i=1;i<=m;i++)
- {
- scanf("%s",ch);
- scanf("%lld",&x);
- if(ch[0]=='A')
- {
- x+=t,x%=d;
- num[++tot]=x,f[tot]=tot;
- while(x>a[cnt].x&&cnt)
- f[a[cnt--].y]=tot;
- a[++cnt].x=x,a[cnt].y=tot;
- }
- else
- {
- x=tot-x+1,t=num[find(x)];
- printf("%lld\n",t);
- }
- }
- return 0;
- }
[JSOI2008]最大数(并查集)
来源: http://www.bubuko.com/infodetail-3392472.html