CF909 div3

发布时间 2023-11-18 23:19:46作者: 沙野博士

CF909 div3

A.Game with Integers

A.Game with Integers
题意
两人博弈,给出一个数字,每人每次可以选择令该数字+1或者-1。如果在10步以内可以令数字为3的倍数,先手胜。否则后手胜。
数据范围
多组数据,\(1 <= T <= 100 , 1 <= n <= 1000\)
题解
后手可以恢复现场,所以先手最多只能有效操作1次。
若+1或者-1一次之后可以被3整除则先手胜,否则后手胜。

#include<bits/stdc++.h>
using namespace std;
int n;
int Solve()
{
	cin >> n;
	if((n - 1) % 3 == 0 || (n + 1) % 3 == 0)
		cout << "First" << '\n';
	else
		cout << "Second" << '\n';	
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0);
	int T; cin >> T; while(T--) Solve();
	return 0;
}

B. 250 Thousand Tons of TNT

B. 250 Thousand Tons of TNT
题意
有n堆炸弹,第i堆的重量为\(a_i\),现在可以用一些车将它们运走。
每辆车恰好可以运k堆,
第一辆车运1,2,3,……,k
第二辆车运k+1,k+2,k+3,……,2k
……
选择一个合适的k,使得运送货物的车辆中,Max{载重最大-载重最小}
k应当是n的约数(k < n)
数据范围
多组数据,\(1 <= T <= 10^4 , 1 <= n <= 150000 , 1 <= a_i <= 10^9\)
题解
枚举n的约数,暴力求最大即可。
约数个数约为log的数量级,复杂度为O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int N = 1.5e5 + 10;
int n;
int Array[N];

long long Calc(int k)
{
	long long Max = 0 , Min = 1e18 , res;
	for(int i = 1 ; i <= n ; i += k)
	{
		res = 0ll;
		for(int j = i ; j - i + 1 <= k ; ++j)
		{
			res += Array[j];
		}
		Max = max(Max , res);
		Min = min(Min , res);
	}
	return Max - Min;
}

int Solve()
{
	long long Answer = 0ll;
	cin >> n;
	for(int i = 1 ; i <= n ; ++i)
		cin >> Array[i];
	for(int i = 2 ; i * i <= n ; ++i)
	{
		if(n % i == 0)
		{
			Answer = max(Answer , Calc(i));
			if(i * i != n)
				Answer = max(Answer , Calc(n / i));
		}
	}
	Answer = max(Answer , Calc(1));
	cout << Answer << '\n';
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T; while(T--) Solve();
	return 0;
}

C. Yarik and Array

C. Yarik and Array
题意
求最大连续子段和,要求连续子段内相邻元素的奇偶性不同。
数据范围
多组数据,\(1 <= n <= 2*10^5 , -10^3 <= a_i <= 10^3\)
题解
本质上就是求最大连续字段和的时候加一个判断。
最大连续子段和的原始Dp如下。

Answer = Dp[0] = 0;
for(int i = 1 ; i <= n ; ++i)
{
    Dp[i] = max(Dp[i-1] , 0) + A[i];
    Answer = max(Anwer , Dp[i]);
    // 以i为结尾的最大连续子段和 
    // = max(续接上以i-1为结尾的最大连续子段和 , 从i开始) 
    // + A[i]
}

之后一般写作简化形式。

Answer = res = 0;
for(int i = 1 ; i <= n ; ++i)
{
    res = max(res , 0) + A[i];
    Answer = max(Answer , res);
}

本题中要增加一个判断,即判断本元素的奇偶性是否和上一个元素的奇偶性不同。

Answer = res = 0;
for(int i = 1 ; i <= n ; ++i)
{
    if(A[i] % 2 != A[i-1] % 2)
        res = max(res , 0);
    else
        res = 0;
    Answer = max(Answer , res += A[i]);
}

完整代码

#include<bits/stdc++.h>
using namespace std;
inline int Abs(int x) { return x < 0 ? -x : x; }
int Solve()
{
	int n , x , lst = -1 , Answer = -1e9 , res;
	cin >> n;
	cin >> lst; res = lst;
	Answer = max(Answer , lst);
	for(int i = 2 ; i <= n ; ++i)
	{
		cin >> x;
		if(Abs(x) % 2 != Abs(lst) % 2)
			res = max(0 , res) + x;
		else
			res = x;
		lst = x;
		Answer = max(Answer , res);
	}
	cout << Answer << '\n';
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T; while(T--) Solve();
	return 0;
}

D. Yarik and Musical Notes

D. Yarik and Musical Notes
题意
给一个长度为n的数组\(a_i\) , \(b_i = 2 ^ {a_i}\)
问有多少<i , j>满足\(i < j , b_i^{b_j} = b_j^{b_i}\)
数据范围
多组数据,\(1 <= n <= 2*10^5,1<=a_i<=10^9\)
题解
手动模拟一下发现除了\(a_i == a_j\)的情况就只有\(a_i , a_j == 1,2\)可以满足条件。


这道题比赛的时候没有TLE,赛后发现TLE20.
map , unordered_map , 数组,这三者消耗的时间比例约为200 : 100 : 1

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int n;
int Array[N];
int Solve()
{
	int num1 = 0 , num2 = 0;
	long long Answer = 0ll;
	cin >> n;
	for(int i = 1 ; i <= n ; ++i) cin >> Array[i];
	sort(Array + 1 , Array + 1 + n);
	for(int i = 1 ; i <= n ; ++i)
	{
		int j = i , cnt = 0;
		while(j <= n && Array[j] == Array[i]) j++ , cnt++;
		Answer += 1ll * cnt * (cnt - 1) / 2;
		if(Array[i] == 1) num1 = cnt;
		if(Array[i] == 2) num2 = cnt;
		i = j - 1;
	}
	Answer += 1ll * num1 * num2;
	cout << Answer << '\n';
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T; while(T--) Solve();
	return 0;
}

E. Queue Sort

E. Queue Sort
题意
一轮操作规定如下:
1.选择数据开头的元素,将其放到数组末尾。
2.将上一步选择的元素不断地与它前方的数据交换,直到该元素严格大于前方元素或者该元素已经到达数组开头。
问多少轮之后可以使数组有序,若永远不能有序则输出-1.
数据范围
多组数据,\(1 <= n <= 2*10^5 , 1 <= a_i <= 10^9\)
输入样例

5
5
6 4 1 2 5
7
4 5 3 7 8 6 2
6
4 3 1 2 6 4
4
5 2 4 2
3
2 2 3

输出样例

2
6
-1
-1
0

题解
其实若能使得数组有序,那么操作次数是固定的。
因为当最小值到达数据开头时,那么之后的操作都是无效操作。
如果这时数组还是无须的,那就是无解。
如果这时数组是有序的,那所操作的轮数就是最小轮数。

因为,如果最小值不在开头,那么数组一定不是完全有序的。

那么如何判断当最小值为开头时,数组是否有序呢?
最小值之前的元素移动之后不会“捣乱”。
因为它们停止移动的条件是,到达开头或者严格大于前方元素。
到达开头即是最小值,不计入讨论。
若其严格大于前方元素,那么假设该元素为x,前方元素为y。
x不会和其后方的元素形成逆序对。

如果x后方有小于x的元素,那么x就不应该在这个位置。

若x和其前方的元素有逆序对,那么y和其前方的元素也有逆序对。

若x和前方的z构成逆序对,即z > x;
因为x > y , 所以 z > y.
所以只判断原来的数据是否产生逆序对即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int n;
int Array[N];

int Solve()
{
	int Min;
	cin >> n;
	for(int i = 1 ; i <= n ; ++i)
		cin >> Array[i];
	Min = Array[1];
	for(int i = 2 ; i <= n ; ++i)
		Min = min(Min , Array[i]);
	for(int i = 1 ; i <= n ; ++i)
		if(Min == Array[i])
		{
			int flag = 1;
			for(int j = i + 1 ; j < n ; ++j)
				if(Array[j] > Array[j+1]) { flag = 0; break; }
			if(flag == 0)
				cout << -1 << '\n';
			else
				cout << i - 1 << '\n';
			break;
		}
	return 0;
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T; while(T--) Solve();
	return 0;
}

F. Alex's whims

F. Alex's whims-1
F. Alex's whims-2
题意
要求构造一个n个节点的树以应付接下来Q个询问。
每次询问要求树种存在某两个叶子节点之间的距离为\(d_i\).
每次询问之前有一次修改的机会,形如 u , x , y.
即断去<u , x>之间的边,并加上<u , y>之间的边。
这要求原本<u , x>之间有边,<u , y>之间没有边。
并且还要求,操作之后还是一颗树。
如果不需要操作输出-1 -1 -1
数据范围
多组数据,\(3 <= n <= 500 , 1 <= Q <= 500 , 2 <= d_i <= n - 1\)
输入样例

3
3 3
2
2
2
5 6
4
2
3
4
3
2
4 9
2
3
3
2
2
2
3
2
2

输出样例

1 2
2 3
-1 -1 -1
-1 -1 -1
-1 -1 -1
1 2
2 3
3 4
4 5
-1 -1 -1
4 3 2
5 4 3
4 2 5
4 5 2
5 3 4
1 2
2 3
3 4
4 3 2
4 2 3
-1 -1 -1
4 3 2
-1 -1 -1
-1 -1 -1
4 2 3
4 3 2
-1 -1 -1

题解
构造一个链,然后拿着n号点伺机连接到相应的点上即可。
比如,n=10,则先构造一个长度为10的链,(1 , 2) , (2 , 3) …… (9 , 10).
然后如果要求存在一个长度为8的叶子节点之间的路径,那么将(9 , 10)断去,然后连接(8 , 10).
如果之后又要求长度为3,那么就断去(8 , 10) , 连接(3 , 10).
……

#include<bits/stdc++.h>
using namespace std;
const int N = 520;
int n , Q;
int d[N];
int Solve()
{
	cin >> n >> Q;
	for(int i = 1 ; i <= Q ; ++i)
		cin >> d[i];
	for(int i = 1 ; i < n ; ++i)
		cout << i << ' ' << i + 1 << '\n';
	d[0] = n - 1;
	for(int i = 1 ; i <= Q ; ++i)
	{
		if(d[i] == d[i-1])
			cout << -1 << ' ' << -1 << ' ' << -1 << '\n';
		else
			cout << n << ' ' << d[i-1] << ' ' << d[i] << '\n';
	}
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T; while(T--) Solve();
	return 0;
}

G.Unusual Entertainment

G.Unusual Entertainment
题意
给出一颗树,以1为根。再给出一个长度为n的排列P。
有Q次询问,每次询问l , r , x,即P[l , r]中是否存在x的后代。
数据范围
多组数据,\(1 <= n , Q <= 10^5\)
样例输入

3
3 5
1 2
2 3
1 2 3
1 2 2
1 2 3
2 3 1
1 2 3
2 3 3
10 10
2 6
2 7
2 4
1 7
2 8
10 6
8 5
9 4
3 4
10 2 5 9 1 7 6 4 3 8
8 9 8
7 8 1
7 10 6
4 8 9
5 5 10
7 10 1
9 9 2
9 10 6
6 6 2
10 10 6
1 1
1
1 1 1

样例输出

YES
NO
YES
NO
YES

NO
YES
YES
YES
NO
YES
YES
NO
NO
NO

YES

题解
先求得dfs序。
一个节点的子树的dfs序是连续的,并且为[dfn[x] , dfn[x] + siz[x] - 1]。
那么问题就是判断区间[l , r]中是否出现过值在[a , b]的数字。
可以用可持久数组/权值线段树操作,(经典操作,不做解释)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n , Dfn_num , rem;
int Siz[N] , Dfn[N] , P[N] , Root[N];
vector<int> G[N];
struct Node{
	int ls , rs , sz;
}Tree[N * 20];
void Init()
{
	Dfn_num = rem = 0;
	for(int i = 1 ; i <= n ; ++i)
		G[i].clear() , Root[i] = 0;
}

void Dfs(int x , int f)
{
	Siz[x] = 1; Dfn[x] = ++Dfn_num;
	for(auto v:G[x])
	{
		if(v ^ f) 
			Dfs(v , x) , Siz[x] += Siz[v];
	}
}

inline int NewNode() 
{
	rem++;
	Tree[rem].ls = Tree[rem].rs = Tree[rem].sz = 0;
	return rem;
}

void Insert(int &rt , int lst , int l , int r , int pos)
{
	if(!rt) rt = NewNode();
	Tree[rt].sz = Tree[lst].sz + 1;
	if(l == r) return ;
	int mid = (l + r) >> 1;
	if(pos <= mid)
		Insert(Tree[rt].ls , Tree[lst].ls , l , mid , pos) , Tree[rt].rs = Tree[lst].rs;
	else
		Insert(Tree[rt].rs , Tree[lst].rs , mid+1 , r , pos) , Tree[rt].ls = Tree[lst].ls;
}

int Query(int rt_L , int rt_R , int l , int r , int x , int y)
{
	if(!rt_R || Tree[rt_R].sz - (rt_L ? Tree[rt_L].sz : 0) == 0) return 0;
	if(x <= l && r <= y)
		return Tree[rt_R].sz - Tree[rt_L].sz;
	int mid = (l + r) >> 1 , Answer = 0;
	if(x <= mid)
		Answer |= Query(Tree[rt_L].ls , Tree[rt_R].ls , l , mid , x , y);
	if(y  > mid)
		Answer |= Query(Tree[rt_L].rs , Tree[rt_R].rs , mid+1 , r , x , y);
	return Answer;
}

int Solve()
{
	int Q , u , v , x;
	cin >> n >> Q; Init();
	for(int i = 1 ; i < n ; ++i)
	{
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	Dfs(1 , 0);
	for(int i = 1 ; i <= n ; ++i)
		cin >> P[i];
	for(int i = 1 ; i <= n ; ++i)
		Insert(Root[i] , Root[i-1] , 1 , n , Dfn[P[i]]);
	while(Q--)
	{
		cin >> u >> v >> x;
		// cout << u << ' ' << v << ' ' << Dfn[x] << ' ' << Dfn[x] + Siz[x] - 1 << '\n';
		if(Query(Root[u - 1] , Root[v] , 1 , n , Dfn[x] , Dfn[x] + Siz[x] - 1))
			cout << "YES" << '\n';
		else
			cout << "NO" << '\n';
	}
	return 0;
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T; while(T--) Solve();
	return 0;
}
// 7 24