P2391 白雪皑皑
是一个比较 dirty 的题。
题意很简单。那么首先就可以想到线段树的区间推平。
注意到 \(n \le 10^7\),那么这个对常数要求很严格。
于是,我们的算法必须是 \(\mathcal{O}(m)\),或者 $ n \log n$ 的。
接着,我们发现对于 \(i\),我们想求的其实是 \(i\) 最后被哪个区间覆盖。
那么,我们倒过来找 \(i\) 第一个被哪个区间覆盖就行了。
那么考虑维护一个链表状物,\(nxt[i]\) 代表 \([i, n]\) 中间,第一个没被染色的点。
每次查询的时候我们更新。
显然更新的过程中,我们可以,也必须使用路径压缩。
分析一下时间复杂度:这相当于一个并查集,被合并 \(n - 1\) 次,那么复杂度是 \(\mathcal{O}(n \log n + m)\),比线段树的要优秀。
// Problem: P2391 白雪皑皑
// Contest: Luogu
// Memory Limit: 512 MB
// Time Limit: 1000 ms
#include <bits/stdc++.h>
#define Misaka namespace
#define Network std
using Misaka Network;
const int N = 1e6 + 7, M = 1e7 + 7;
int nxt[N], n, m, p, q, col[N];
int find(int x){return (nxt[x] == x ? x : nxt[x] = find(nxt[x]));}
signed main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> p >> q;
for(int i = 1; i <= n + 1; i ++) nxt[i] = i;
for(int i = m; i >= 1; i --){
int l = (i * p + q) % n + 1;
int r = (i * q + p) % n + 1;
if(l > r) swap(l, r);
for(int j = find(l); j <= r; j = find(j)){
col[j] = i, nxt[j] = j + 1;
}
}
for(int i = 1; i <= n; i ++) cout << col[i] << "\n";
return 0;
}
本文来自博客园,作者:Trent900,转载请注明原文链接:https://www.cnblogs.com/GE9X/p/21536838

浙公网安备 33010602011771号