[Codeforces Round #395 (Div. 1)] - E Timofey and our friends animals

26 篇文章 0 订阅
11 篇文章 0 订阅
洛谷传送门
Codeforces传送门

题目大意

给你一个 n n n个点 m m m条边的无向图, 任意一条边 a i → b i a_i\to b_i aibi满足 ∣ a i − b i ∣ ≤ k |a_i-b_i|\le k aibik,给出 q q q组询问, 每次询问 [ l , r ] [l,r] [l,r]内节点构成的子图的连通块个数。

输入输出格式

输入格式

第一行三个正整数 n , m , k n,m,k n,m,k

以下 m m m行, 每行两个正整数 a i , b i a_i,b_i ai,bi

以下一行一个正整数 q q q

下面 q q q行, 每行两个正整数 l i , r i l_i, r_i li,ri表示一组询问。

输出格式

对于每组询问, 输出一个正整数表示连通块个数。

输入输出样例

输入样例#1:
5 3
3
1 3
2 3
4 5
5
1 1
1 2
2 3
1 3
1 5
输出样例#1:
1
2
1
1
2

解题分析

还以为 k k k有什么用… 直接 L C T LCT LCT大力搞就好了啊。

对所有边和询问按右端点排序, 然后维护最大生成树, 定义一条边的权值为两端点编号较小的那个, 用树状数组维护权值出现的次数, 查询直接查权值 ≥ l \ge l l的生成树上的边有几条。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <climits>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define MX 600500
#define gc getchar()
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define lbt(i) ((i) & (-(i)))
#define dad tree[now].fat
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int n, m, cnt, top, k, q, tot;
struct Node {int son[2], fat, val, mn, pos; bool rev;} tree[MX];
struct OPT {int l, r, id;} edge[MX], que[MX];
int fr[MX], to[MX], sta[MX], ans[MX], Tree[MX];
IN bool operator < (const OPT &x, const OPT &y) {return x.r < y.r;}
IN void add(R int pos, R int del)
{for (; pos <= n; pos += lbt(pos)) Tree[pos] += del;}
IN int query(R int pos)
{
	int ret = 0;
	for (; pos; pos -= lbt(pos)) ret += Tree[pos];
	return ret;
}
IN bool get(R int now) {return tree[dad].son[1] == now;}
IN bool nroot(R int now) {return tree[dad].son[1] == now || tree[dad].son[0] == now;}
IN void pushup(R int now)
{
	tree[now].pos = now, tree[now].mn = tree[now].val;
	if (ls) if (tree[ls].mn < tree[now].mn) tree[now].mn = tree[ls].mn, tree[now].pos = tree[ls].pos;
	if (rs) if (tree[rs].mn < tree[now].mn) tree[now].mn = tree[rs].mn, tree[now].pos = tree[rs].pos;
}
IN void pushrev(R int now) {std::swap(ls, rs), tree[now].rev ^= 1;}
IN void pushdown(R int now)
{
	if (tree[now].rev)
	{
		pushrev(ls), pushrev(rs);
		tree[now].rev = false;
	}
}
IN void rotate(R int now)
{
	R bool dir = get(now);
	R int fa = dad, grand = tree[fa].fat;
	tree[fa].son[dir] = tree[now].son[dir ^ 1];
	tree[tree[now].son[dir ^ 1]].fat = fa;
	tree[now].fat = grand;
	if (nroot(fa)) tree[grand].son[get(fa)] = now;
	tree[fa].fat = now;
	tree[now].son[dir ^ 1] = fa;
	pushup(fa);
}
IN void splay(R int now)
{
	int tmp = sta[top = 1] = now, fa;
	W (nroot(now)) sta[++top] = now = dad;
	W (top) pushdown(sta[top--]);
	now = tmp;
	W (nroot(now))
	{
		fa = dad;
		if (nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
		rotate(now);
	}
	pushup(now);
}
IN void access(R int now)
{
	for (R int x = 0; now; x = now, now = dad)
	splay(now), rs = x, pushup(now);
}
IN void makeroot(R int now) {access(now), splay(now), pushrev(now);}
IN void split(R int x, R int y) {makeroot(x); access(y); splay(y);}
IN void link(R int x, R int y) {makeroot(x); tree[x].fat = y;}
IN void cut(R int x, R int y)
{
	split(x, y);
	tree[x].fat = tree[y].son[0] = 0;
	pushup(y);
}
IN int findroot(R int now)
{
	access(now), splay(now);
	W (ls) pushdown(now), now = ls;
	return now;
}
int main(void)
{
	int foo, bar, tar;
	in(n), in(k), in(m); cnt = n;
	for (R int i = 1; i <= n; ++i) tree[i].val = tree[i].mn = INT_MAX;
	for (R int i = 1; i <= m; ++i)
	{
		in(edge[i].l), in(edge[i].r);
		if (edge[i].l > edge[i].r) std::swap(edge[i].l, edge[i].r);
	}
	in(q);
	for (R int i = 1; i <= q; ++i) in(que[i].l), in(que[i].r), que[i].id = i;
	std::sort(edge + 1, edge + 1 + m);
	std::sort(que + 1, que + 1 + q);
	R int cur = 1;
	for (R int i = 1; i <= q; ++i)
	{
		W (cur <= m && edge[cur].r <= que[i].r)
		{
			foo = findroot(edge[cur].l), bar = findroot(edge[cur].r);
			if (foo ^ bar)
			{
				++cnt;
				tree[cnt].val = tree[cnt].mn = edge[cur].l;
				link(edge[cur].l, cnt), link(edge[cur].r, cnt);
				fr[cnt] = edge[cur].l, to[cnt] = edge[cur].r;
				add(edge[cur].l, 1); ++tot;
			}
			else
			{
				split(edge[cur].l, edge[cur].r);
				tar = tree[edge[cur].r].pos;
				if (tree[edge[cur].r].mn < edge[cur].l)
				{
					cut(tar, fr[tar]), cut(tar, to[tar]);
					add(fr[tar], -1);
					++cnt;
					tree[cnt].val = tree[cnt].mn = edge[cur].l;
					link(edge[cur].l, cnt), link(edge[cur].r, cnt);
					fr[cnt] = edge[cur].l, to[cnt] = edge[cur].r;
					add(edge[cur].l, 1);
				}
			}
			++cur;
		}
		ans[que[i].id] = que[i].r - que[i].l + 1 - (tot - query(que[i].l - 1));
	}
	for (R int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
}

PS:此题貌似有 N k Nk Nk做法? 不是很懂…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值