4336: BJOI2015 骑士的旅行

4336: BJOI2015 骑士的旅行

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 282   Solved: 123
[ Submit][ Status][ Discuss]

Description

在一片古老的土地上,有一个繁荣的文明。
这片大地几乎被森林覆盖,有N座城坐落其中。巧合的是,这N座城由恰好N-1条双
向道路连接起来,使得任意两座城都是连通的。也就是说,这些城形成了树的结构,任意两
座城之间有且仅有一条简单路径。
在这个文明中,骑士是尤其受到尊崇的职业。任何一名骑士,都是其家族乃至家乡的荣
耀。Henry从小就渴望成为一名能守护家乡、驱逐敌人的骑士。勤奋训练许多年后,Henry
终于满18岁了。他决定离开家乡,向那些成名已久的骑士们发起挑战!
根据Henry的调查,大陆上一共有M名受封骑士,不妨编号为1到M。
第i个骑士居住在城Pi,武力值为Fi。
Henry计划进行若干次旅行,每次从某座城出发沿着唯一的简单路径前往另一座城,
同时会挑战路线上武力值最高的K个骑士(Henry的体力有限,为了提高水平,当然要挑
战最强的骑士)。如果路线上的骑士不足K人,Henry会挑战遇到的所有人。
每次旅行前,可能会有某些骑士的武力值或定居地发生变化,Henry自然会打听消息,
并对计划做出调整。
为了在每次旅行时做好充分准备,Henry希望你能帮忙在每次旅行前计算出这条路线
上他将挑战哪些对手。

Input

第一行,一个整数N,表示有N座城,编号为1~N。
接下来N-1行,每行两个整数Ui和Vi,表示城Ui和城Vi之间有一条道路相连。
第N+1行,一个整数M,表示有M个骑士。
接下来M行,每行两个整数Fi和Pi。按顺序依次表示编号为1~M的每名骑士的武
力值和居住地。
第N+M+2行,两个整数Q,K,分别表示操作次数和每次旅行挑战的骑士数目上限。
接下来Q行,每行三个整数Ti,Xi,Yi。Ti取值范围为{1,2,3},表示操作类型。
一共有以下三种类型的操作:
Ti=1时表示一次旅行,Henry将从城Xi出发前往城市Yi;
Ti=2时表示编号为Xi的骑士的居住地搬到城Yi;
Ti=3时表示编号为Xi的骑士的武力值修正为Yi。

Output

输出若干行,依次为每个旅行的答案。
对每个Ti=1的询问,输出一行,按从大到小的顺序输出Henry在这次旅行中挑战的
所有骑士的武力值。如果路线上没有骑士,输出一行,为一个整数-1。

Sample Input

5
1 2
1 3
2 4
2 5
4
10 1
6 1
14 5
7 3
5 3
1 2 3
1 5 3
1 4 4
2 1 4
1 2 3

Sample Output

10 7 6
14 10 7
-1
7 6

HINT

100%的数据中,1 ≤ N, M ≤ 40,000,1 ≤ Ui, Vi, Pi ≤ N,1 ≤ Q ≤ 80,000, 1 ≤ K ≤ 

20,旅行次数不超过 40,000 次,武力值为不超过1,000的正整数。 

Source

[ Submit][ Status][ Discuss]

一开始写了个树剖+treap,常数爆炸,GG
后来膜拜了Mektpoy大神
LCT+配对堆
复杂度只需O(kqlogn)
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

struct Knight{
	int Atk,Num;
	Knight(){}
	Knight(int Atk,int Num): Atk(Atk),Num(Num){}
	bool operator < (const Knight &b) const {return Atk < b.Atk;} 
};

const int maxn = 4E4 + 40;
const int INF = ~0U>>1;
typedef __gnu_pbds::priority_queue<Knight,less<Knight>,__gnu_pbds::pairing_heap_tag> Heap;

int n,m,q,K,ch[maxn][2],rev[maxn],fa[maxn],pfa[maxn],Atk[maxn],ansnum[maxn]
	,Liv[maxn],va[maxn],Max[maxn],pos[maxn],ansAtk[21],anspos[21];
Heap Q[maxn];
Heap::point_iterator id[maxn];

void pushdown(int x)
{
	if (rev[x]) {
		swap(ch[x][0],ch[x][1]);
		if (ch[x][0]) rev[ch[x][0]] ^= 1;
		if (ch[x][1]) rev[ch[x][1]] ^= 1;
		rev[x] ^= 1;
	}
}

void maintain(int x)
{
	Max[x] = va[x]; pos[x] = x;
	for (int i = 0; i < 2; i++)
		if (ch[x][i] && Max[ch[x][i]] > Max[x])
			Max[x] = Max[ch[x][i]],pos[x] = pos[ch[x][i]];
}

void rotate(int x)
{
	int y = fa[x],z = fa[y];
	pfa[x] = pfa[y]; pfa[y] = 0;
	int d = ch[y][0] == x?0:1;
	ch[y][d] = ch[x][d^1]; maintain(y);
	fa[ch[y][d]] = y; ch[x][d^1] = y;
	fa[x] = z; fa[y] = x; maintain(x);
	if (z) ch[z][ch[z][1] == y] = x,maintain(z);
}

stack <int> s;
void splay(int x)
{
	for (int now = x; now; now = fa[now]) s.push(now);
	while (!s.empty()) {pushdown(s.top()); s.pop();};
	for (int y; y = fa[x]; rotate(x))
		if (fa[y])
			rotate((ch[fa[y]][0] == y)^(ch[y][0] == x)?x:y);
}

void Access(int x)
{
	for (int u = x,v = 0; u; v = u,u = pfa[u]) {
		splay(u);
		if (ch[u][1]) fa[ch[u][1]] = 0,pfa[ch[u][1]] = u;
		ch[u][1] = v; maintain(u);
		if (v) fa[v] = u,pfa[v] = 0;
	}
}

void ChangeRoot(int x) {Access(x); splay(x); rev[x] ^= 1;}
void Join(int x,int y) {ChangeRoot(x); pfa[x] = y; splay(y);}

void Rebuild(int x)
{
	ChangeRoot(x);
	Knight k = Q[x].top();
	va[x] = k.Atk; maintain(x);
}

int getint()
{
	char c = getchar();
	int ret = 0;
	while (c < '0' || '9' < c) c = getchar();
	while ('0' <= c && c <= '9') ret = ret*10 + c - '0',c = getchar();
	return ret;
}

int main()
{
	//freopen("DMC.txt","r",stdin);
	n = getint();	
	for (int i = 1; i <= n; i++) {
		va[i] = Max[i] = -INF; pos[i] = i; 
		Q[i].push(Knight(-INF,0));
	}
	
	for (int i = 1; i < n; i++) {
		int x = getint(),y = getint();
		Join(x,y);
	}
	m = getint();
	for (int i = 1; i <= m; i++) {
		Atk[i] = getint(); Liv[i] = getint();
		id[i] = Q[Liv[i]].push(Knight(Atk[i],i));
		Rebuild(Liv[i]);
	}
	q = getint(); K = getint();
	while (q--) {
		int typ = getint();
		if (typ == 1) {
			int l = getint(),r = getint(),tot = 0;
			for (int i = 1; i <= K; i++) {
				ChangeRoot(l); Access(r); splay(r);
				if (Max[r] > 0) {
					ansAtk[++tot] = Max[r];
					anspos[tot] = pos[r];
					Knight k = Q[anspos[tot]].top();
					ansnum[tot] = k.Num;
					Q[anspos[tot]].pop(); 
					Rebuild(anspos[tot]);
				}
				else break;
			}
			if (!tot) {puts("-1"); continue;}
			else {
				for (int i = 1; i <= tot; i++) {
					printf("%d ",ansAtk[i]);
					id[ansnum[i]] = Q[anspos[i]].push(Knight(ansAtk[i],ansnum[i]));
					Rebuild(anspos[i]);
				}
				puts("");
			}
		}
		else if (typ == 2) {
			int num = getint(),to = getint();
			Q[Liv[num]].erase(id[num]);
			Rebuild(Liv[num]);
			id[num] = Q[Liv[num] = to].push(Knight(Atk[num],num));
			Rebuild(Liv[num]);
		}
		else {
			int num = getint(),modi = getint();
			Q[Liv[num]].erase(id[num]);
			id[num] = Q[Liv[num]].push(Knight(Atk[num] = modi,num));
			Rebuild(Liv[num]);
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值