CF909 div3
CF909 div3
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
题意
有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
题意
求最大连续子段和,要求连续子段内相邻元素的奇偶性不同。
数据范围
多组数据,\(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
题意
给一个长度为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
题意
一轮操作规定如下:
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
题意
要求构造一个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
题意
给出一颗树,以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