A.2016(规律,十进制快速幂)
题目链接:A.2016
题意:
给出二维矩阵,求 A的n次方, (1≤n<10^100000)
解题思路:
由于n很大,题目中有关键提示:
Feel free to think why the problem is called `2016`,所以此题的运算应该与2016有关,具体怎么推出来的我也不太清楚 ,欢迎指教
做法一:
#include<iostream>
#include<string>
using namespace std;
int main(){
string s;
int a, a1, a2, b, b1, b2, c, c1, c2, d, d1, d2, i, n;
cin >> s;
n = 0;
int len = s.size();
for(i = 0; i < len; i++){
n = (n*10 + (s[i] - '0'))%2016;
}
cin >> a >> b >> c >> d;
if(n == 0){
a = 1; b = 0; c = 0; d = 1;
}
a1 = a; b1 = b; c1 = c; d1 = d;
for(i = 1; i < n; i++){
a2 = a; b2 = b; c2 = c; d2 = d;
a = (a1 * a2 + b1 * c2) % 7;
b = (a1 * b2 + b1 * d2) % 7;
c = (c1 * a2 + d1 * c2) % 7;
d = (c1 * b2 + d1 * d2) % 7;
}
cout << a << " " << b << endl;
cout << c << " " << d << endl;
return 0;
}
做法二:十进制快速幂
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 100005
#define mod 7
char str[MAXN];
struct mat{
int m[3][3];
}base,ans;
mat multiply(mat a,mat b){
mat temp;
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
temp.m[i][j] = 0;
for(int k = 0; k < 2; k++){
temp.m[i][j]=(temp.m[i][j] + a.m[i][k] * b.m[k][j]) % mod;
}
}
}
return temp;
}
mat quick_mod(LL n){
ans.m[0][0] = ans.m[1][1] = 1;
ans.m[1][0] = ans.m[0][1] = 0;
while(n){
if(n&1)
ans = multiply(ans,base);
base = multiply(base,base);
n >>= 1;
}
return ans;
}
int main(){
int num;
while(scanf("%s", str)!=EOF){
num = 0;
int len = strlen(str);
for(int i = 0; i < len; i++)
num=(num * 10 + str[i] - '0') % 2016;
scanf("%d%d",&base.m[0][0], &base.m[0][1]);
scanf("%d%d",&base.m[1][0], &base.m[1][1]);
base = quick_mod(num);
printf("%d %d\n",base.m[0][0], base.m[0][1]);
printf("%d %d\n",base.m[1][0], base.m[1][1]);
}
return 0;
}
B.Gambling
题目链接:B.Gambling
题意:
给a个红色气球,b个绿色气球,c个蓝色气球,不放回的取,取出全部红色气球得一等奖,取出全部绿色气球得二等奖,取出全部蓝色气球得三等奖。问获得一等奖的概率,获得二等奖的概率,获得三等奖的概率
解题思路:
#include<cstdio>
#include<algorithm>
using namespace std;
long long x, y, z;
void cal(long a, long b, long c, int p) {
x = b * c * (2 * a + b + c);
y = (a + b + c) * (a + b) * (a + c);
z = __gcd(x, y);
printf("%lld/%lld%c", x / z, y / z, p < 3 ? ' ' : '\n');
}
int main() {
long long a, b, c;
scanf("%lld%lld%lld", &a, &b, &c);
cal(a, b, c, 1);
cal(b, a, c, 2);
cal(c, a, b, 3);
return 0;
}
C.Hamiltonian Path(思维模拟)
题目链接:C.Hamiltonian Path
题意:
给n个点,m条边,求从1走到n的最短距离, 1只能走2, 2 只能走3,后面同理
解题思路:
只需用一个数组记录从当前点到后一个点的最短距离,然后求和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
int a[maxn];
int main(){
int n, m, ans = 0;
cin >> n >> m;
memset(a, INF, sizeof(a));
for(int i = 1; i <= m; i++){
int u, v, w;
cin >> u >> v >> w;
if(v - u == 1){ // 如果相差为1,则记录最短距离
a[u] = min(a[u], w);
}
}
for(int i = 1; i < n; i++){
if(a[i] == INF){ // 此路不通
cout << -1 << endl;
return 0;
}
else{
ans = ans + a[i];
}
}
cout << ans << endl;
return 0;
}
G.Rolling Variance(思维)
题目链接:G.Rolling Variance
题意:
求相邻m个数的值,公式为
解题思路:
设平均值为b
化简sqrt(a[1] * a[1] - 2 * a[1] * b + b * b + a[2] * a[2] - 2 * a[2] * b + b * b + .......... + a[m] * a[m] - 2 * a[m] * b + b * b)/m-1
= sqrt( a[1] * a[1] + a[2] * a[2] + ... + a[m] * a[m] + m * b * b - 2* b * (a[1] + a[2] + .... a[m] ))
这样只需求a[i] * a[i] 的前缀和, a[i] 的前缀和就行
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
double a[maxn], b[maxn];
int main(){
int n, m;
cin >> n >> m;
a[0] = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
b[i] = b[i-1] + a[i] * a[i];
a[i] = a[i] + a[i-1];
}
for(int i = m; i <= n; i++){
double k = a[i] - a[i-m];
double ab = k / m;
double fang = b[i] - b[i-m];
cout << sqrt((fang + (double)m * ab * ab - 2 * k * ab ) / (double)(m - 1)) << endl;
}
return 0;
}
H.Super Fast Fourier Transform(思维)
题目链接:H.Super Fast Fourier Transform
题意:
给出两个数组。求
(a[i], b[i] ≥ 0,a[1] + a[2]+⋯+a[n], b[1] + b[2]+…,b[m] ≤ 10^6)
解题思路:
由给出的数据范围,分析出数组a和数组b中会出现相当多重复的数字,所以不必一个一个加,只需判断个数,相同的乘以数量即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + 5;
ll a1[maxn], b1[maxn];
ll a[maxn], b[maxn];
int main(){
ll n, m, k, len1 = 1, len2 = 1;
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> k;
if(a[k] == 0){
a1[len1] = k;
len1++;
}
a[k]++;
}
for(int j = 1; j <= m; j++){
cin >> k;
if(b[k] == 0){
b1[len2] = k;
len2++;
}
b[k]++;
}
ll ans = 0;
for(int i = 1; i < len1; i++){
for(int j = 1; j < len2; j++){
ans += (ll)sqrt(abs(a1[i] - b1[j])) * a[a1[i]] * b[b1[j]];
}
}
cout << ans << endl;
return 0;
}
J.Defense Tower(贪心)
题目链接: J.Defense Tower
题意:
给出n个防御塔的伤害值,n-1条边(代表哪两个防御塔相邻),需要摧毁所有防御塔,攻击当前防御塔,当前的防御塔不会攻击你,而和此防御塔相邻的塔会攻击你。求摧毁所有防御塔受到的最小伤害。
解题思路:
想要摧毁所有防御塔,一定会受到n-1次攻击,那么只要先攻击防御塔伤害最高的,那么他就不会打你,然后依次更新被攻击掉的和他相邻的防御塔伤害就可以
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
struct node{
int val, pos;
}a[maxn];
ll sum[maxn]; // 攻击此防御塔会受到的伤害
bool cmp(node k, node u){
return k.val > u.val;
}
vector<int> ve[maxn];
int main(){
int n, ans = 0;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i].val;
a[i].pos = i;
}
for(int i = 1; i < n; i++){
int v, u;
cin >> v >> u;
ve[u].push_back(v);
ve[v].push_back(u);
sum[v] += a[u].val;
sum[u] += a[v].val;
}
sort(a+1, a+1+n, cmp);
for(int i = 1; i <= n; i++){
ans += sum[a[i].pos];
int len = ve[a[i].pos].size();
for(int j = 0; j < len; j++){
sum[ve[a[i].pos][j]] -= a[i].val;
}
}
cout << ans << endl;
return 0;
}