2021牛客寒假算法基础集训营5

美丽的路径
链接:https://ac.nowcoder.com/acm/contest/9985/A
来源:牛客网

题目描述

叶妹妹非常喜欢图论题,这天她出了一个图论题,有一个nn个点mm条边的无向图,其中第ii个点的点权为a_ia
i

,她定义一条点数为kk路径:b_1b
1

,b_2b
2

,…,b_kb
k

;其中点b_{i-1}b
i−1

与点b_ib
i

通过一条边直接相连(2\le i\le k)(2≤i≤k),所以路径中可以出现重复的点。她将一条路径的美丽值定义为:假设这条路径的点数为kk,那么这条路径的美丽值就是此路径上的所有点的点权中第\lfloor k/2+1 \rfloor⌊k/2+1⌋小的点权。现在她会询问你是否存在起点为ss号点并且终点为tt号点的路径,如果存在则先输出YESYES,再输出存在的所有路径中的最大的美丽值;否则直接输出NONO。

输入描述:
有多组样例,第一行输入一个数TT,表述样例组数

对于每组样例,第一行先输入四个数nn,mm,ss,tt,保证1\le s\le n,1\le t\le n1≤s≤n,1≤t≤n

第二行输入nn个数,其中第ii个数为a_ia
i

,表示第ii个点的点权

接下来输入mm行,第ii行输入两个数x_ix
i

和y_iy
i

,表示点x_ix
i

与点y_iy
i

之间有一条无向边,保证1\le x_i\le n,1\le y_i\le n1≤x
i

≤n,1≤y
i

≤n

【数据规模与约定】

1\le T\le10,1\le n\le 210^5,0\le m\le210^5,1\le a_i\le10^91≤T≤10,1≤n≤2∗10
5
,0≤m≤2∗10
5
,1≤a
i

≤10
9

输出描述:
如果存在起点为ss号点并且终点为tt号点的路径,则第一行输出YESYES,第二行输出存在的所有的路径中的最大的美丽值;否则直接输出NONO
示例1
输入
复制
1
5 4 2 4
1 2 3 4 5
1 2
2 3
3 4
4 5
输出
复制
YES
4

第几小的最大的我们优先考虑二分,如果是no拿并查集判断即可,如果是yes的话,我们可以二分那个美丽度,判断这个美丽度是否满足条件,如果把大于等于美丽度的边为1,小于美丽度的边为-1,如果从起点走到终点的最长路的边长是大于等于0的,即大于等于这个美丽度的边的个数是小于等于这个美丽度的边的个数的话,那么这个美丽度就符合条件,中间需要注意的是,如果有两个连续的边大于等于这个美丽度的话那么就是符合条件的,如果存在正环也是符合条件的

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 4e5 + 10;
const int M = 3 * N;
const int mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1); 

int n, m, s, t;
int h[N], e[N], ne[N], idx;
int a[N], p[N], dist[N], cnt[N];
bool st[N];

void add(int a, int b){
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

int find(int x){
	if (x != p[x])    p[x] = find(p[x]);
	return p[x];
}

bool spfa(int mid){
	memset(dist, -0x3f, sizeof dist);
	memset(st, 0, sizeof st);
	memset(cnt, 0, sizeof cnt);
	queue<int> q;
	
	dist[s] = (a[s] < mid ? -1 : 1);
	cnt[s] = 0;
	q.push(s);
	
	int num = 0;
	while(q.size()){
		int t = q.front();
		q.pop();
		
		num ++;
		if (num >= n * 2)   return true;
		
		st[t] = false;
		int val = (a[t] < mid ? -1 : 1);
		
		for (int i = h[t]; ~i; i = ne[i]){
			int  j = e[i];
			
			int w = (a[j] < mid ? -1 : 1);
			
			if (w >= 1 && val >= 1)   return true;
			if (dist[j] < dist[t] + w){
				dist[j] = dist[t] + w;
				cnt[j] = cnt[t] + 1;
				
				if (j == t && dist[t] >= 0)   return true;
				if (cnt[j] >= n)   return true;
				
				if (!st[j]){
					st[j] = true;
					q.push(j);
				}
			}
		}
	}
	
	return dist[t] >= 0;
}

bool check(int mid){
	return spfa(mid);
}

signed main(){
    ios;
    int T;
    cin >> T;
    while(T --){
    	memset(h, -1, sizeof h);
    	idx = 0;
    	
    	cin >> n >> m >> s >> t;
    	int l = 1e9, r = 0;
    	for (int i = 1; i <= n; i ++){
    		cin >> a[i];
    		p[i] = i;
    		
    		l = min(l, a[i]), r = max(r, a[i]);
		}
		
		for (int i = 1; i <= m; i ++){
			int a, b;
			cin >> a >> b;
			add(a, b), add(b, a);
			
			int pa = find(a), pb = find(b);
			if (pa != pb){
				p[pa] = pb;
			}
		}
		
		if (find(s) != find(t)){
			cout << "NO" << endl;
			continue;
		}
		
		while(l < r){
			int mid = (l + r + 1) / 2;
			if (check(mid))    l = mid;
			else r = mid - 1;
		}
		
		cout << "YES" << endl;
		cout << l << endl;
	}
	
	return 0;
}

比武招亲(上)
链接:https://ac.nowcoder.com/acm/contest/9985/B
来源:牛客网

题目描述
众所周知,天姐姐只喜欢天下最聪明的人,为了找到这样的人,她决定比武招亲!

只见天姐姐在榜上留下了这样一道问题,谁做出来了就可以俘获她的芳心!

爱慕天姐姐已久的泽鸽鸽问询赶来,只见榜上写着:

给定 n,mn,m,定义一种序列,构造方法如下:

1.1. 在 [1,n][1,n] 中任意选择 mm 次,得到了 mm 个整数(显然数字可能相同);

2.2. 将选出的 mm 个数字排序之后得到一个序列 { a_{1},a_{2},…,a_{m} }{a
1

,a
2

,…,a
m

}。

定义一个序列的贡献为 max{ a_{1},a_{2},…,a_{m} }-min{ a_{1},a_{2},…,a_{m} }max{a
1

,a
2

,…,a
m

}−min{a
1

,a
2

,…,a
m

},求所有本质不同的序列的贡献和。

为了防止结果过大,将答案为 998244353998244353 取模后输出。

(对于两个序列长度为m的序列 A、B,若 {\exists}i∈[1,m],A_i≠B_i∃i∈[1,m],A
i



=B
i

,则序列 A、B 本质不同)

泽鸽鸽心有余而力不足,而你作为他最好的基友决定帮助泽鸽鸽俘获美人心!

现在,这个重任就交给你啦!

输入描述:
一行输入两个正整数 n,m
【数据规模与约定】
1 <= n, m <= 5*10^5
输出描述:
一行一个整数,为答案对 998244353 取模后的结果。
示例1
输入
复制
3 2
输出
复制
4
说明
本质不同的序列有如下几种:1 1、2 2、3 3、1 2、1 3、2 3,贡献为 0+0+0+1+2+1=4。

枚举差值,这个差值的对数有多少对,然后的关键问题是有m个数,第一个和最后一个已经确定了,中间能选的数必须大于等于最小的数,小于等于最大的数,并且非递减的,所以的话,就是每两个数的差可以是0,也可以是非1的别的数,现在把板子的意义为加1,有差值个板子,有m-2 + 差值个位置,任意选差值个地方放板子,所以结果如此

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second


 
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

const int N = 2e6 + 500;
const int M = 3 * N;
const int mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1); 

int n, m;
int fact[N], infact[N];

int qmi(int a, int b){
	int res = 1;
	while(b){
		if (b & 1)    res = res % mod * a % mod;
			a = a % mod * a % mod;
			b >>= 1;
	   }
	   return res % mod;
}

void init(){
	fact[1] = 1;
	//infact[1] = 1;
	infact[0] = 1;
	for (int i = 2; i < N; i ++){
		fact[i] = fact[i - 1] * i % mod;
		fact[i] %= mod;
	}
	infact[N - 1] = qmi(fact[N - 1], mod - 2);
	for (int i = N - 2; i >= 1; i --){
		infact[i] = infact[i + 1] % mod * (i + 1) % mod;
		infact[i] %= mod;
	}
}

int C(int a, int b){
	return fact[a] % mod * infact[a - b] % mod * infact[b] % mod;
}

signed main(){
  	ios;
  	init();
  	cin >> n >> m;
  	int ans = 0;
  //	cout << fact[5] << endl;
  	//cout << C(5, 2) << endl;
  	//cout << C(1, 1) << endl;
  	for (int i = 1; i <= n - 1; i ++){
  		int cnt = n - i;
  	//	cout << cnt << "* " << C(m - 2 + i, i) << endl;
  		ans = ans % mod + i % mod * cnt % mod * (C(m - 2 + i, i)) % mod;
  		ans %= mod;
	}
	
	cout << ans << endl;
  
    return 0;
}

石子游戏

链接:https://ac.nowcoder.com/acm/contest/9985/D
来源:牛客网

题目描述
叶妹妹很喜欢玩石头,于是这天泽鸽鸽给她出了一道石子游戏,规则是这样的:有nn堆石子排成一行,其中第ii堆石子有a_ia
i

个,叶妹妹可以选择做无数次这种操作:每次操作把连续相邻的kk个石子堆中的每堆石子数目加一,请问叶妹妹能否让每堆石子的数目都相同呢?叶妹妹觉得这题太简单了,于是丢给了聪明的你,快来解决这个问题吧!

输入描述:
第一行输入样例组数TT

对于每组样例来说,第一行输入两个数nn和kk

第二行输入nn个数,其中第ii个数为a_ia
i

【数据规模与约定】

1\le T\le 10,1\le n\le10^5,1\le k\le n,0\le a_i\le 10^91≤T≤10,1≤n≤10
5
,1≤k≤n,0≤a
i

≤10
9

输出描述:
输出总共TT行,对于每组样例来说,如果能使每堆石子的数目都相同则输出一个整数xx,xx表示达到相同时的最少的操作数;否则输出-1−1

示例1
输入
复制
1
4 3
1 1 1 2
输出
复制
1

如果是满足条件的话,那么那个最后的数一定不是很大,所以可以枚举最后的数,依次看是否符合条件即可,或者就是发掘性质,每种的每次都是加1

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e5 + 10;
const int M = 3 * N;
const int mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1); 

int a[N], temp[N];
int n, k;

bool check(int mid){
	memcpy(temp, a, sizeof a);
	for (int i = 1; i + k - 1 <= n; i ++){
		if (temp[i] < mid){
			int cha = mid - temp[i];
			for (int j = i; j <= i + k - 1; j ++){
				temp[j] += cha;
			}
		} 
	}
	
	for (int i = 1; i <= n; i ++){
		if (temp[i] != mid)   return false;
	}
	return true;
}

signed main(){
	ios;
	int T;
	cin >> T;
	while(T --){
		cin >> n >> k;
		int maxd = 0;
		for (int i = 1; i <= n; i ++){
			cin >> a[i];
			maxd = max(maxd, a[i]);
		}
		
		int flag = -1;
		for (int i = maxd; i <= maxd; i ++){
			if (check(i)){
				flag = i;
                break;
			}
		}
		
		int ans = 0;

		
		if (flag == -1){
			cout << "-1" << endl;
		}
		else{
			for (int i = 1; i <= n; i ++){
				ans += (flag - a[i]);
			}
			cout << ans / k << endl;
		}
		
		
	}
		
	return 0;
}

树上博弈
链接:https://ac.nowcoder.com/acm/contest/9985/E
来源:牛客网

题目描述
叶妹妹非常喜欢在树上玩博弈游戏,这天叶妹妹和泽鸽鸽遇到了一个有趣的树上博弈游戏。

游戏规则是:给定一颗有nn个点的无根树,第ii个点的权值为a_ia
i

,叶妹妹与泽鸽鸽轮流执行操作。

操作如下:

选择一个度数为11的节点(即叶子节点),自己的得分加上该点的权值
删去这个节点以及该节点的所有邻接的边
当树上没有点了则表示游戏结束,现在规定由叶妹妹先执行操作,双方都想让自己得分与对方得分的差值尽可能大(即尽可能让自己多得分,并且尽可能让对方少得分),假设叶妹妹和泽鸽鸽都以最优的策略玩这个游戏,求游戏结束时叶妹妹与泽鸽鸽分数的差值是多少?

输入描述:
第一行输入一个整数TT,表示样例的组数

每组样例的第一行输入一个整数nn

第二行输入nn个整数,第ii个数为a_ia
i

,表示第ii个点的点权为a_ia
i

接下来输入n-1n−1行,每行输入两个整数uu,vv,表示树上有一条从点uu到点vv的无向边(1\le u\le n,1\le v\le n)(1≤u≤n,1≤v≤n)

保证输入一定是一棵树

【数据规模与约定】

1 \le T \le 50,1 \le n \le 20,-10^9\le a_i\le 10^91≤T≤50,1≤n≤20,−10
9
≤a
i

≤10
9

输出描述:
输出TT行,每行输出一个数字xx,xx表示游戏结束叶妹妹与泽鸽鸽分数的差值
示例1
输入
复制
1
6
4 3 6 5 2 1
1 3
2 3
3 4
3 5
5 6
输出
复制
3
说明
对于这个样例,如果双方都以最优策略操作,一种可能的情况是:

叶妹妹选择44号点,其分数加55分,并删除该节点及该节点的所有邻接的边。
泽鸽鸽选择11号点,其分数加44分,并删除该节点及该节点的所有邻接的边。
叶妹妹选择66号点,其分数加11分,并删除该节点及该节点的所有邻接的边。
泽鸽鸽选择22号点,其分数加33分,并删除该节点及该节点的所有邻接的边。
叶妹妹选择33号点,其分数加66分,并删除该节点及该节点的所有邻接的边。
泽鸽鸽选择55号点,其分数加22分,并删除该节点及该节点的所有邻接的边,游戏结束。

最后,叶妹妹总得分为1212分,而泽鸽鸽总得分为99分,叶妹妹与泽鸽鸽分数的差值为33分。

从最开始的状态递归,返回的是如果这个人是这种状态的话,他和对面差的最大值是多少,然后记忆化递归处理即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

const int N = 1e5;
const int M = 3 * N;
const int mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1); 

int f[1 << 22];
bool st[1 << 22];
int a[N];
int n;
int h[N], e[N], ne[N],idx;

void add(int a, int b){
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

int dfs(int val){
	if (!val)      return 0;
	if (st[val])   return f[val];
	st[val] = true;
	
	f[val] = -INF;
	
	for (int i = 0; i < n; i ++){
		if ((val >> i) & 1){
			int temp = 0;
			for (int k = h[i]; ~k; k = ne[k]){
				int j = e[k];
				if ((val >> j) & 1)    temp ++;
			}
			
			if (temp > 1)   continue;
			
			f[val] = max(f[val], a[i] - dfs(val ^ (1 << i)));
		}
	}
	
	return f[val];
}

signed main(){
  	int T;
  	cin >> T;
  	while(T --){
  		memset(st, 0, sizeof st);
  		idx = 0;
  		memset(h, -1, sizeof h);
		cin >> n;
		for (int i = 0; i < n; i ++)     cin >> a[i];
		
		for (int i = 1; i < n; i ++){
			int a, b;
			cin >> a >> b;
			a --;
			b --;
			add(a, b), add(b, a);
		}	
		
		cout << dfs((1 << n) - 1) << endl;
	}
  
    return 0;
}

我的心是冰冰的
链接:https://ac.nowcoder.com/acm/contest/9985/F
来源:牛客网

题目描述
泽鸽鸽很喜欢王冰冰,为了证明他是否配得上冰冰,叶妹妹出了一道题来考他:给定了一棵有nn个点的树,你需要对树的每个点进行染色,且要求每两个相邻(即有边相连)的点颜色不同,叶妹妹想知道至少需要拥有多少种不同的颜色才能完成这种染色?泽鸽鸽觉得这题太简单了,于是聪明的你快来解答吧!

输入描述:
第一行输入一个整数TT,表示样例的组数

每组样例的第一行输入一个整数nn

接下来输入n-1n−1行,每行输入两个整数uu和vv,表示树上有一条从点uu到点vv的无向边(1\le u\le n,1\le v\le n)(1≤u≤n,1≤v≤n)

保证输入一定是一棵树

【数据规模与约定】

1 \le T \le 50,1 \le n \le 10^51≤T≤50,1≤n≤10
5

输出描述:
输出TT行,每行输出一个整数xx,xx表示至少需要的颜色种类数
示例1
输入
复制
1
3
1 2
2 3
输出
复制
2
说明
只需要两种颜色:把11号点和33号点染成同一种颜色,而22号点染成另一种颜色即可。

不管什么图,最多的颜色是4

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 5e4 + 10;
const int M = 3 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1); 

signed main(){
	int T;
	cin >> T;
	while(T --){
		int n;
		cin >> n;
		for (int i = 1; i < n; i ++){
			int a, b;
			cin >> a >> b;
		}
		
		if (n > 1)   cout << "2" << endl;
		else   cout << "1" << endl;
	}	
	
	return 0;
}

模仿游戏
链接:https://ac.nowcoder.com/acm/contest/9985/G
来源:牛客网

题目描述
有一天叶妹妹和泽鸽鸽他们一起参加一个模仿游戏,这个游戏主要任务是打怪兽,每个怪兽都有一个权值,如果两个怪兽的权值相同,那么表示这两个怪兽是同一类型的怪兽。对于任何一个怪兽,只能由一个人去打败它,并且一个人无法同时打两只及两只以上的怪兽。泽鸽鸽和叶妹妹打败任何一个怪兽都只需要花费一分钟,并且他们只能在整数分钟去打怪兽,不能出现小数分钟。由于这是模仿游戏,因此叶妹妹只能模仿泽鸽鸽去打败怪兽。比如,当泽鸽鸽在之前已经打败了权值为xx的怪兽后,叶妹妹才能去打权值为xx的怪兽(即相同类型的怪兽)。现在给出每个怪兽的出现时间和权值,请你设计一个最优的顺序使得所有怪兽都被打败后的时间最早!

输入描述:
第一行输入一个数TT,表示样例组数

对于每组样例,第一行先输入一个数nn

接下来输入nn行,每行输入两个数a_ia
i

和b_ib
i

,a_ia
i

表示第ii个怪兽出现的时间,b_ib
i

表示第ii个怪兽的权值

【数据规模与约定】

1\le T\le 20,1\le n\le 10^5,1\le a_i\le 10^5,1\le b_i\le n1≤T≤20,1≤n≤10
5
,1≤a
i

≤10
5
,1≤b
i

≤n

输出描述:
输出TT行,每行输出一个数xx,表示最优顺序下所有怪兽都被打败后的最早时间
示例1
输入
复制
1
4
1 1
1 1
2 2
2 2
输出
复制
3

这个题很明显是贪心,最明显的就是每次让哥哥打以前没有打过的,然后如果某个时刻同时有两个,打哪个,很明显是打下一个先来的那个,如何实现,标记每个时间有哪些怪兽,然后依次枚举时间的所有怪兽,如果这个怪兽以前没有被杀死过,就把她放入堆中,如果这个怪兽以前被杀死了,就让妹妹需要杀死的怪兽加1,然后如果堆中没有的话,就是哥哥妹妹都杀tot,否则的话,就让哥哥杀第一次出现的,妹妹杀一个,并且把这个之前出现过的加进去

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 2e5 + 10;
const int M = 3 * N;
const int mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1); 

int n, m, k;
int have[N], ne[N];
bool kills[N];

vector<int> v[N];

struct Node{
	int id, ne;
	bool operator<(const Node &W)const{
		return ne > W.ne;
	}
};

priority_queue<Node> heap;

signed main(){
   ios;
   int T;
   cin >> T;
   while(T --){
   		int n;
   		cin >> n;
   		memset(kills, 0, sizeof kills);
   		memset(have, 0, sizeof have);
   		
   		for (int i = 1; i < N;i ++)   v[i].clear();
   		
   		int max_t = 0;
   		for (int i = 1; i <= n; i ++){
   			int a, b;
   			cin >> a >> b;
   			
   			max_t = max(max_t, a);
   			v[a].push_back(b);
		}
		
		for (int i = 1; i <= n; i ++)   ne[i] = inf;
		for (int i = 1; i <= max_t; i ++){
			for (int j = 0; j < v[i].size(); j ++){
				if (++have[v[i][j]] == 2){
					ne[v[i][j]] = i;
				}
			}
		}
		
		memset(have, 0, sizeof have);
		
		int ans = 0, tot = 0;
		for (int i = 1; i <= max_t || heap.size(); i ++){
			ans ++;
			
			for (int j = 0; j < v[i].size(); j ++){
				if (!have[v[i][j]])     heap.push({v[i][j], ne[v[i][j]]});
				have[v[i][j]] ++;
				if (kills[v[i][j]])   tot ++;
			}
			
			if (!heap.size()){
				tot = max(tot - 2, 0ll);
				continue;
			}
			
			tot --;
			tot = max(tot, 0ll);
			kills[heap.top().id] = true;
			tot += (have[heap.top().id]) - 1;
			heap.pop();
		}
		
		cout << ans + (tot + 1) / 2 << endl;
   }
	
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值