//这个是构造题。。用dsu 来进行枚举i的🔥超爱的 感觉dsu又雄起了。。。
// 他要求[l,r]区间要有x个1 。。可以用树状数组来查询 之前已经填充的量。然后
// 这里有个贪心思想就是 能填右边的就右边先填。。因为右边的1 可能被后面的区间覆盖到。。也就是可以多贡献几次。
// 这样反过来说 1就会尽可能少。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll __int128_t
#define ar array<int, 2>
#define arr array<int, 3>
int n, m, k, inf = 1LL << 61, mod = 998244353;// 1e9+7;
const int N = 5e5 + 50;
int tr[N], K;
void add(int x, int v) {
while (x <= K) {//这个K变量是开放的。。是个隐患。。
tr[x] += v, x += x & (-x);
}
}
int query(int x) {
int s = 0;
while (x) {
s += tr[x], x -= x & (-x);
}
return s;
}
struct DSU {
std::vector<int> f, sz;
DSU(int n) : f(n + 1), sz(n + 1, 1) { std::iota(f.begin(), f.end(), 0); }
int leader(int x) {
if (x == f[x]) return x;
f[x] = leader(f[x]);
//sz[f[x]] += sz[x];//一个dfs后序位置。。实际上已经是带权并查集的合并了。
//sz[x] = 0;
return f[x];
}
bool same(int x, int y) { return leader(x) == leader(y); }
bool merge(int x, int y) { //这个是把y并到x的子树上。。有方向的。细节。
x = leader(x);
y = leader(y);
if (x == y)
return false;
sz[x] += sz[y];
//sz[y]=0;
f[y] = x;
return true;
}
int size(int x) { return sz[leader(x)]; }
};
void solve() {
cin >> n >> m;
K = n;//这个k是我书树状数组的max。。树状数组还没有找到合适的封闭的模版 泪目。
arr a[m];
for (auto&[y, x, z] : a)//我们这里把y放在第一权重. 这样排序 就不需要自定义规则
cin >> x >> y >> z;
sort(a, a + m);
DSU uf(n);
vector<int>ans(n + 1);
for (auto[y, x, z] : a) {
int t = query(y) - query(x - 1);
if (t >= z)
continue;
z -= t;
for (int i = uf.leader(y); z--; i = uf.leader(i)) {
add(i, 1);
ans[i] = 1;
uf.merge(i - 1, i);//细节: 这里的合并是有方向的 也就是不能改变i-1 和i的位置 。我们要把f[i]=i-1这样的。往前走
}
}
for (int i = 1; i <= n; ++i)
cout << ans[i] << ' ';
};
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout << fixed << setprecision(15);
#ifdef DEBUG
freopen("../1.in", "r", stdin);
#endif
//init_f();
//init();
//expr();
// int T; cin >> T; while(T--)
solve();
return 0;
}
这里dsu来枚举i 的靠谱性。dsu本质上就是 树。。而路径压缩 本质上就是改变数结构 变成菊花树
这题是特化了 它是一个链 。
然后我们又规定了merge的方向 以下标i小的为根。。
所以他每个联通的部分全部🔥会指联通块内 下标最小的位置 以之为根。所以。稳定性ok的。