九校联考-DL24 凉心模拟 Day2T3 欠钱 (money)

题目描述

南极的企鹅王国大学中生活着 n n n 只企鹅,作为 21 21 21 世纪的优秀大学生,企鹅们积极响应“大众创业,万众创新”的号召,纷纷创业。但是创业需要资金,企鹅们最近手头比较紧,只能互相借钱。

企鹅的借钱行为是有规律可循的:每只企鹅只会借一次钱,并且只会从一只企鹅那里借钱。借钱关系中不存在环(即不存在类似“金企鹅欠银企鹅钱,银企鹅欠铜企鹅钱,铜企鹅欠金企鹅钱”这种情况)。

企鹅的还钱行为也是有规律可循的:每只企鹅一旦新获得了一笔钱,就会立刻用这笔钱尽可能偿还自己欠的债务,直到债务偿清或用光这笔钱。它只会使用新获得的这笔钱,至于以前它有没有钱、有多少钱,与还钱行为无关。

企鹅们经常会做美梦。在一只企鹅 A A A 的梦里,它梦见自己创业成功,一下子获得了 + ∞ +∞ + 元钱,于是(按照上文的还钱规则)它赶快把钱用来还债,接着拿到钱的那只企鹅也赶快把钱用来还债……如此往复,直到所有获得钱的企鹅都完成了还债操作。梦醒之后,它开心地把梦的内容告诉了另外一只企鹅 B B B,企鹅 B B B 听了,也很开心,于是它问道:在你的梦里,我获得了多少钱呢?(指 B B B 去还债之前手里的钱,包括后来用于还债的钱和还债后 B B B 手里剩下的钱。)

梦毕竟是梦,对实际的欠债情况没有影响。

输入输出格式

输入格式

第一行两个整数 n n n m m m,表示有 n n n 只企鹅, m m m 个操作。

接下来 m m m 行,有两种可能的格式:

0 a b c:修改操作,企鹅 a a a 向企鹅 b b b 借了 c c c 元钱。1 a b:查询操作,询问假如 a a a 有了 + ∞ +∞ + 元钱,企鹅 b b b 会净收入多少钱。

本题强制在线,也就是说:对于每个操作输入的变量 a , b , c a, b, c a,b,c(如果没有 c c c,那就只有 a , b a, b a,b)都不是实际的 a , b , c a, b, c a,b,c,想获得实际的 a , b , c a, b, c a,b,c 应当经过以下操作:

a = ( a + l a s t a n s ) % n + 1 ; a = (a + lastans) \% n + 1; a=(a+lastans)%n+1;

b = ( b + l a s t a n s ) % n + 1 ; b = (b + lastans) \% n + 1; b=(b+lastans)%n+1;

c = ( c + l a s t a n s ) % n + 1 ; c = (c + lastans) \% n + 1; c=(c+lastans)%n+1;

其中, l a s t a n s lastans lastans 是上一次询问的答案。如果没有上一次询问, l a s t a n s lastans lastans 0 0 0

输出格式

对每个询问操作,输出一行一个数表示答案。

输入输出样例

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

解题分析

一句话题意: 链上最小值带 l i n k link link操作。

直接大力 L C T LCT LCT就好了,但是拆边为点会 T T T掉一个点。

我们可以将边的权值下放到下端的点, 然后查询的时候查上方点的左儿子的信息就好了。

注意 s p l i t split split操作后需要重新 m a k e r o o t makeroot makeroot, 因为这是一棵有根树。

据说还可以用倍增+启发式合并做, 不过我太蒟了只敢写 L C T LCT LCT的QAQ

代码如下:

#include <cstdio>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define MX 100050
inline char nc()
{
    static const int buflen=1e6;
    static char buf[buflen],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,buflen,stdin),p1==p2)?EOF:*p1++;
}
void out(int x)
{
	if(x>9)out(x/10);
	putchar(x%10+'0');
}
#define gc nc()
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 dot, cnt, lastans, q, top;
int sta[MX], bel[MX];
struct Node {int son[2], mn, val, fat; bool rev;} tree[MX];
int find(R int now) {return bel[now] == now ? now : bel[now] = find(bel[now]);}
namespace LCT
{
	#define ls tree[now].son[0]
	#define rs tree[now].son[1]
	#define dad tree[now].fat
	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 pushrev(R int now) {std::swap(ls, rs), tree[now].rev ^= 1;}
	IN void pushdown(R int now) {if(tree[now].rev) tree[now].rev = false, pushrev(ls), pushrev(rs);}
	IN void pushup(R int now)
	{
		tree[now].mn = tree[now].val;
		if(ls) tree[now].mn = std::min(tree[now].mn, tree[ls].mn);
		if(rs) tree[now].mn = std::min(tree[now].mn, tree[rs].mn);
	}
	IN void rotate(R int now)
	{
		R int fa = dad, grand = tree[fa].fat;
		R bool dir = get(now);
		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[now].son[dir ^ 1] = fa;
		tree[fa].fat = now;
		pushup(fa);
	}
	IN void splay(R int now)
	{
		R int tmp = now, fa;
		sta[top = 1] = now;
		W (nroot(now)) sta[++top] = now = dad;
		W (top) pushdown(sta[top--]);
		now = tmp;
		W (nroot(now))
		{
			fa = dad;
			if(nroot(fa)) rotate(get(fa) == get(now) ? fa : now);
			rotate(now);
		}
		pushup(now);
	}
	IN int access(R int now)
	{
		R int x = 0;
		for (; now; x = now, now = dad)
		splay(now), rs = x, pushup(now);
		return x;
	}
	IN int lca(R int x, R int y) {access(x); return access(y);}
	IN void link(R int x, R int y) {splay(x); tree[x].fat = y;}
	IN void makeroot(R int x) {access(x), splay(x), pushrev(x);}
	IN void split(R int x, R int y) {makeroot(x), access(y), splay(y);}
	#undef ls
	#undef rs
	#undef dad
}
int main(void)
{
	int opt, a, b, c;
	in(dot), in(q);
	for (R int i = 1; i <= dot; ++i) bel[i] = i, tree[i].val = tree[i].mn = 100000000;
	W (q--)
	{
		in(opt), in(a), in(b);
		a = (a + lastans) % dot + 1, b = (b + lastans) % dot + 1;
		if(!opt) in(c), c = (c + lastans) % dot + 1;
		if(!opt)
		{
			bel[find(a)] = find(b); tree[a].val = tree[a].mn = c;
			LCT::link(a, b);
		}
		else
		{
			if(find(a) != find(b)) {out(lastans = 0); puts(""); continue;}
			if(LCT::lca(a, b) == b)
			{
				int rt = bel[b];
				LCT::split(a, b);
				out(lastans = tree[tree[b].son[0]].mn); puts("");
				LCT::makeroot(rt);
			}
			else out(lastans = 0), puts("");
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值