2020省赛第八次训练赛题解
文章目录
- 2020省赛第八次训练赛题解
-
- 前面的碎碎念
- [A - Team Formation](https://vjudge.net/problem/ZOJ-3870)
- [B - Convex Hull](https://vjudge.net/problem/ZOJ-3871)
- [C - Beauty of Array](https://vjudge.net/problem/ZOJ-3872)
- [D - Lunch Time](https://vjudge.net/problem/ZOJ-3875)
- [E - May Day Holiday](https://vjudge.net/problem/ZOJ-3876)
- [F - Convert QWERTY to Dvorak](https://vjudge.net/problem/ZOJ-3878)
- [G - Capture the Flag](https://vjudge.net/problem/ZOJ-3879)
- [H - Demacia of the Ancients](https://vjudge.net/problem/ZOJ-3880)
- [I - Ace of Aces](https://vjudge.net/problem/ZOJ-3869)
- [J - Earthstone Keeper](https://vjudge.net/problem/ZOJ-3877)
前面的碎碎念
这场比赛的题目不是很难,签到题占了快一半了,同样的记录本场比赛的原因是因为本场比赛的思维题占了一半,思维题可遇不可求,每一次遇到都大开眼界,吐槽一下hdu和fzu竟然在国庆期间不开放,导致前面几场比赛没机会写题解,(其实也是没时间,每天一场训练赛对于我这种慢性子是需要很长很长很长的时间来补题的)你看看人家poj和zoj假期依然坚守岗位,在这里为两大oj点赞,当然牛客和洛谷也是坚守岗位。冲冲冲!!
A - Team Formation
思维题
题目分析:题目的意思就是有一些人,两两组队,每个人都有能力值,如果两个人能力值的异或值大于每一个人单独的能力值,那么这两个人组的队可以认为是一个出色的团队。
首先这个题目暴力是不会出奇迹的,暴力是可以过样例的但是显然也会超时,那么我们可以从异或出发,两个数的异或就是二进制的每一位异或,我们遵循相同为0,相异为1,也就是说如果两个数对应位置上的数字不同那么结果就是1,回到题目中,题目要求的是异或两个数之后变大,那么我们以其中一个数为基准,如果这个数二进制的某一位上为0,而对应另一个数相应位置并且这个位置是最高位二进制上为1,那么两个数异或就会变大,不用去考虑其他位,因为就算其他位置异或变小了但是我们保证高位异或变大整体还是变大的。
对于任意的a(以下用二进制来表示)
如a
1011001
对应的b可以是
1* * * * *
1* *
1*
对于b
若b的最高位 对于 a来说 在a的当前位 为0 则符合上述条件
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
int vis[maxn], a[maxn];
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n;
memset(vis, 0, sizeof(vis));
scanf("%d", &n);
ll ans = 0;
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
int x = a[i], tot = 0;
while (x)
{
++tot;
x >>= 1;
}
vis[tot]++; //因为首位一定为1,那么就记录一下这个数最高位为1的位置
}
for (int i = 1; i <= n; ++i)
{
int x = a[i], tot = 0;
while (x)
{
++tot;
if (!(x & 1)) //如果这个数当前位置为0,看看能找到几个数当前位置也是这个数二进制最高位为1
//并且这种过滤已经排除掉本身了,所以不用判断是不是异或本身
ans += vis[tot];
x >>= 1;
}
}
printf("%lld\n", ans);
}
}
B - Convex Hull
计算几何,凸包
题意:给n个点,|x[i]|,|y[i]| <= 1e9。求在所有情况下的子集下(子集点数>=3),凸包的面积和。
这题主要有几个方面,一个是凸包的面积,可以直接用线段的有向面积和求得,这个当时没想到。还有就是,在180度以内的点数。
所以实际上是枚举2个点作为某个凸包的一条边,看有多少个这样的凸包。那个2^num - 1是保证除了2个点外至少还需1个点才能构成凸包。
时间复杂度O(n * n * logn)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
const double pi = acos(-1.0);
#define ll long long
#define maxn 1010
#define mod 998244353
ll area2(ll ax, ll ay, ll bx, ll by)
{
return ((ax * by % mod - ay * bx % mod) % mod + mod) % mod;
}
struct node
{
ll x, y;
double ang;
bool operator<(const node &b) const
{
return ang < b.ang;
}
} vec[maxn << 1];
ll p2[maxn];
int main()
{
p2[0] = 1;
for (ll i = 1; i <= 1000; ++i)
p2[i] = (p2[i - 1] << 1) % mod;
ll t;
scanf("%lld", &t);
while (t--)
{
ll n;
scanf("%lld", &n);
ll x[maxn], y[maxn];
for (ll i = 0; i < n; ++i)
scanf("%lld%lld", x + i, y + i);
ll ans = 0;
for (ll i = 0; i < n; ++i)
{
for (ll j = 0; j < n; ++j)
{
vec[j].x = x[j];
vec[j].y = y[j];
vec[j].ang = atan2(x[j] - x[i], y[j] - y[i]);
}
vec[i] = vec[n - 1];
for (ll j = 0; j < n - 1; ++j)
{
vec[j + n - 1] = vec[j];
vec[j + n - 1].ang += pi * 2;
}
ll nn = n - 1 + n - 1;
sort(vec, vec + nn);
ll l = 0, r = 0;
while (l < n - 1)
{
while (r < nn && vec[r].ang - vec[l].ang < pi)
r++;
ll area = area2(x[i], y[i], vec[l].x, vec[l].y);
ll cnt = (p2[r - l - 1] - 1 + mod) % mod;
ans = (ans + 1ll * area * cnt % mod) % mod;
++l;
}
}
printf("%lld\n", (mod - ans) % mod);
}
return 0;
}
C - Beauty of Array
dp
参考链接
题目分析:这个题目很巧妙,题目要求的是所有连续序列中不同数字之和,出看可能不好写,也不知道怎么去写,暴力肯定会超时的,所以不用考虑。那么我们每添加一个元素进来,求当前序列包含a的所有序列之和,我们用两个变量dp,sum,dp去储存当前所有子序列的和,sum储存整个序列的和,然后用一个变量去定义每个数字最终出现的位置。
说是这么说,当我第一次看题解的时候,我没看懂,然后就自己手动模拟了一下代码的过程,大致模拟完之后,也就知道代码的意思了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
typedef long long ll;
int a[maxn];
int main()
{
int t, m, x;
scanf("%d", &t);
while (t--)
{
memset(a, 0, sizeof(a));
scanf("%d", &m);
ll ans = 0, dp = 0;
for (int i = 1; i <= m