A:
题意: A和B猜拳,问A获胜次数
题解: 求获胜次数,只需关心获胜次数,依次判断A出石头剪刀布的获胜次数即可,注意 3e9爆 int
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a, b, c, x, y, z;
cin >> a >> b >> c >> x >> y >> z;
long long sum = 0;
if(a >= y) sum += y;
else sum += a;
if(b >= z) sum += z;
else sum += b;
if(c >= x) sum += x;
else sum += c;
cout << sum << endl;
return 0;
}
B:
题意: 求给定字符串中重新排序后可以得到的 616这个字符串个数。
题解: 答案即 min(cnt6−1,cnt1)
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
char s[N]; int n;
int main()
{
scanf("%d%s", &n, s + 1);
int six = 0, one = 0;
for(int i = 1; i <= n; i++) {
if(s[i] == '1') one++;
if(s[i] == '6') six++;
}
six--;
if(one >= six) printf("%d\n", six);
else printf("%d\n", one);
return 0;
}
C:
题意: 给你每个题 mod 109+7下的概率值,现在问你恰好做出 [0,n]题的概率。
题解: 预处理每个题未做出的概率, dp[i][j]表示前 i题做出来 j题的概率,输出 dp[n][0∼n]
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2010;
const ll mod = 1e9 + 7;
ll t[N], f[N]; int n;
ll dp[N][N];
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld", &t[i]);
f[i] = ((1 - t[i]) % mod + mod) % mod;
}
dp[0][0] = 1;
for(int i = 1; i <= n; i++){
dp[i][0] = dp[i - 1][0] * f[i] % mod;
for(int j = 1; j <= i; j++)
dp[i][j] = (dp[i - 1][j] * f[i] % mod + dp[i - 1][j - 1] * t[i] % mod) % mod;
}
for(int j = 0; j <= n; j++) printf("%lld%c", dp[n][j], " \n"[j == n]);
return 0;
}
D:
题意: 给定 n个点,问你可以构成多少个钝角三角形。
题解: O(n3)枚举三个点,判断是否构成钝角三角形。1.不共线即构成三角形,2.存在两边平方和小于第三边平方。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define x first
#define y second
const int N = 510;
typedef pair<ll, ll> PLL;
PLL p[N]; int n;
ll dis(PLL A, PLL B){
return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y);
}
bool jud(PLL A, PLL B, PLL C){
ll a[3];
a[0] = dis(A, B), a[1] = dis(A, C), a[2] = dis(B, C);
sort(a, a + 3);
if(a[2] > a[1] + a[0] && sqrt(a[0]) + sqrt(a[1]) > sqrt(a[2])) return true;
return false;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
ll a, b; scanf("%lld%lld", &a, &b);
p[i] = make_pair(a, b);
}
ll res = 0;
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
for(int k = j + 1; k <= n; k++)
if(jud(p[i], p[j], p[k])) res++;
printf("%lld\n", res);
}
E:
题意: i+j=k,其中 i,j,k都是正整数,且 i∗j≤n,求 (i,j,k)这样的三元组共多少个,其中 1≤n≤4∗107。
题解: 两边平方得 i+j+2ij=k,那么 i,j和i∗j必须是完全平方数,由于 i∗j也是完全平方数,且 (i,j)和(j,i)只是两者位置交换,故我们令 i>j。枚举 x到 n, x为 ij,每层枚举 y到 x,判断 x∗x%y 是否为 0,若 x∗x%y=0,则得到一组 (i,j)=(y,x∗x%y)和 (j,i)=(x∗x%y,y)
(写这么详细是因为赛时没反应过来被坑了太久)
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int main()
{
scanf("%d", &n);
long long ans = 0;
for(int i = 1; i * i <= n; i++) {
for(int j = 1; j < i; j++)
if(i * i % j == 0) ans += 2;
ans ++;//(i==j)
}
printf("%lld\n", ans);
return 0;
}
F:
题意: 现在有 n个物品,每个物品有两个属性值 a和 b,牛牛选一个物品得到的分数是该物品属性值 a的值,牛可乐选一个物品得到的分数是该物品属性值 b的值,牛牛先手,现在两人都采用最优策略,使得自己和对方的总分数差更大,求分配方案。(有 spj)
题解: 令 V,W分别为牛牛和牛可乐的总分数,则 V−W越大越好。现在假设选完后牛牛发现如果选其他物品自己与牛可乐的分数差可以更大,而牛可乐比较笨,同意更换。设牛牛拿出的更换物价值为 a1,b1,牛可乐拿出的更换物价值为 a2,b2,更换后两人总分数分别为 V′=V−a1+a2,W′=W−b2+b1,则有 V′−W′>V−W,化简下可得 a1+b1<a2+b2。那么两人在选物品时一定优先选择属性值之和 a+b更大的,按两属性值之和从大到小排序然后间隔一个输出即可。
代码:
#include<bits/stdc++.h>
using namespace std;
struct V{
int v, num;
bool operator < (const V &A) const {
return v > A.v;
}
}t[200010];
int main()
{
int n; scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &t[i].v);
for(int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
t[i].v += x; t[i].num = i;
}
sort(t + 1, t + 1 + n);
for(int i = 1; i <= n; i += 2) printf("%d%c", t[i].num, " \n"[i + 1 >= n]);
for(int i = 2; i <= n; i += 2) printf("%d%c", t[i].num, " \n"[i + 1 >= n]);
return 0;
}
G:
这个题比较玄学,不理解为什么要指定模数,不作题解。
H:
题意: 给定一个长度为 n的序列,每次取这段序列中的部分数字所需魔法值为序列中最大值与最小值之差,且每次至少取 k个,问你怎么取会使得消耗魔法值最小。
题解: 显然是按照排序后的结果依次判断取最好, f[i]表示取前 i个数所需的最小魔法值。
状态转移方程为: f[i]=j∈[0,i−k]min{f[j]+a[i]−a[j+1]}
所以维护 j∈[0,i−k]min{f[j]−a[j+1]}即可
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 300010;
int q[N], n, k;
long long f[N];
int main()
{
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%d", &q[i]);
sort(q + 1, q + 1 + n);
long long last = -q[1];
//last = min{f[j]-a[j + 1]}, 0 ≤ j ≤ i - k,初始化为f[0] - a[1]= -a[1]
for(int i = 1; i < k; i++) f[i] = 1e9 + 7;
for(int i = k; i <= n; i++) {
f[i] = last + q[i];
last = min(last, f[i - k + 1] - q[i - k + 2]);
}
printf("%lld\n", f[n]);
return 0;
}
I:
题意: 给 n个点,两点之间路径长度为 lowbit(vi⊕vj),求使得 n个点连通的最小花费。
题解: 由于相同的数异或为 0,故进行去重,令去重后共 t个数之后再枚举记录所有的数的二进制位是 0还是 1,然后找 0和 1都存在的最小二进制位 k,这样异或后,答案即 2k−1×(g−1)
证明: 当 t个数的前 k−1位每位都相同,则异或都为0, (g−1)个lowbit自然也大于等于 2k−1∗(g−1),所以找到任意一位的0和1都存在则所有位都可以异或得1时,得到最小的答案,即 2k−1∗(g−1)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int a[N], b[N], n, g;
bool bit[35][2];
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; ) {
b[++g] = a[i];
int j = i + 1;
while(j <= n && a[j] == a[i]) j++;
i = j;
}
for(int i = 1; i <= g; i++) {
int j = b[i], g1 = 0;
while(g1 < 30) {
bit[g1][j % 2] = true;
g1++;
j /= 2;
}
}
int i;
for(i = 0; i < 30; i++) if(bit[i][0] && bit[i][1]) break;
printf("%lld\n", (1ll << i) * (g - 1));
}
J
待补