首先,感谢中国石油大学的出题组,题的质量很高,很适合刚入门但有一定提高的ACMer。传送门
本题解顺序由易到难(个人主观因素),共12道题,但是由于本蒟蒻水平优先,暂时做了7道。
问题 D: 大数
题解:寻找最小循环子序列,利用kmp中的next数组进行计算。
则证明他是存在最小循环子序列。
本题有一个坑点,就是这些数都是由比他小的某个数重复构成,所以要特判。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 1000005;
int net[maxn];
int main()
{
//freopen("in.in","r",stdin);
//freopen("out.out","w",stdout);
ios::sync_with_stdio(false);
string a;
cin >> a;
int len = a.size();
int i = 0, j = -1;
net[0] = -1;
while (i < len)
{
if (j == -1 || a[i] == a[j])
{
i++;
j++;
net[i] = j;
}
else
j = net[j];
}
if (len % (len - net[len]) == 0)
{
int tmp = len - net[len];
if (net[len])
for (int i = 0; i < tmp; i++)
cout << a[i];
else
cout << -1;
cout << endl;
}
else
cout << -1 << endl;
//fclose(stdin);
//fclose(stdout);
return 0;
}
问题 B: 奎奎发红包
题解:贪心算法。类似于排队接水问题,但又不太一样。
我们假设a排在b前面:
。
化简得:
,我们就构造:
,越小则说明优先度越高。
坑点:当
时要进行特判。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 100005;
ll ans = 0;
int n;
struct node
{
int v, t;
double value; //t/v
} a[maxn];
bool cmp(node x, node y)
{
return x.value < y.value;
}
int main()
{
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i].v >> a[i].t;
if (a[i].v) //特判
a[i].value = (a[i].t * 1.0) / a[i].v;
else //当v==0时,value趋近正无穷
a[i].value = 0x3f3f3f;
}
sort(a + 1, a + 1 + n, cmp); //结构体排序
ll sum_t = 0;
for (int i = 1; i <= n; i++)
{
sum_t += a[i].t;
ans += (ll)a[i].v * sum_t;
}
cout << ans << endl;
return 0;
}
问题 I: 星区划分
题解:并查集+遍历搜索。写两个for循环,遍历点之间的关系,然后进行7个判断,若符合则进行并查集操作。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int n, m, ans;
int s[1005]; //并查集
bool flag[10];
struct star
{
int x, y, z, v;
} a[1005];
int find_set(int x) //找到父结点,压缩状态
{
if (x != s[x])
s[x] = find_set(s[x]);
return s[x];
}
void union_set(int x, int y) //合并
{
x = find_set(x);
y = find_set(y);
if (x != y)
s[x] = s[y];
}
void solve(int i, int j)
{
if (a[i].x == a[j].x && a[i].y == a[j].y && abs(a[i].z - a[j].z) == 1) //上下
{
if (abs(a[i].v - a[j].v) <= m)
union_set(i, j);
}
if (abs(a[i].x - a[j].x) == 1 && a[i].y == a[j].y && a[i].z == a[j].z) //左右
{
if (abs(a[i].v - a[j].v) <= m)
union_set(i, j);
}
if (a[i].x == a[j].x && abs(a[i].y - a[j].y) == 1 && a[i].z == a[j].z) //前后
{
if (abs(a[i].v - a[j].v) <= m)
union_set(i, j);
}
if (a[i].x == a[j].x && a[i].y == a[j].y && a[i].z == a[j].z) //本身
{
if (abs(a[i].v - a[j].v) <= m)
union_set(i, j);
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i].x >> a[i].y >> a[i].z >> a[i].v;
s[i] = i; //并查集初始化
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if (i == j)
continue;
solve(i, j);
}
for (int i = 1; i <= n; i++)
if (s[i] == i)
++ans;
cout << ans << endl;
return 0;
}
问题 A: sciorz画画
题解:区间dp,
代表从
到
的
边形,而若是要构成
边形,则需要
。利用记忆优化+深搜的思想进行函数的构建。
当我们确定
与
后,我们设
,使得
,代表的含义是在
点进行切割,一分为二。这就推出了状态转移方程:
。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int a[105];
ll dp[105][105];
ll js(int i, int j, int k)
{
return a[i] * a[j] * a[k];
}
int main()
{
int t, cnt = 0;
cin >> t;
while (t--)
{
memset(dp, 0, sizeof(dp));
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = n - 2; i >= 1; i--)
for (int j = i + 2; j <= n; j++)
for (int k = i + 1; k <= j - 1; k++)
dp[i][j] = max(dp[i][k] + dp[k][j] + js(i, j, k), dp[i][j]);
printf("Case #%d: %lld\n", ++cnt, dp[1][n]);
}
return 0;
}
问题 K: 数学问题
题解:打表+二位前缀和记录答案。注意两个个坑点:①是:
。
②是:由于
所以前缀和的
为下一次的前缀和提供
,实在不理解可以画图进行模拟一下。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 2002;
int t, g, n, m;
ll c[maxn][maxn];
ll ans[maxn][maxn];
void init()
{
c[1][1] = 1;
for (int i = 0; i <= 2000; i++)
c[i][0] = 1;
for (int i = 1; i <= 2000; i++)
for (int j = 1; j <= i; j++)
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % g;
}
void get_ans()
{
for (int i = 2; i <= 2000; i++) //g>1
{
for (int j = 1; j <= i; j++)
{
ans[i][j] = ans[i - 1][j] - ans[i - 1][j - 1] + ans[i][j - 1];
if (c[i][j] == 0)
ans[i][j]++;
}
ans[i][i + 1] = ans[i][i]; //坑点2
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> t >> g;
init();
get_ans();
while (t--)
{
cin >> n >> m;
m = min(n, m); //坑点1
cout << ans[n][m] << endl;
}
return 0;
}
问题 F: 求和
题解:矩阵构造+矩阵快速幂取模。我记着这个矩阵的构造是线性代数课本上的原题。
我们可以构造矩阵:
则:
剩下的就是矩阵快速幂取模的事了。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const ll mod = 1000000007;
int n;
struct Matrix
{
ll m[70][70];
Matrix() //初始化
{
memset(m, 0, sizeof(m));
}
};
Matrix Multi(Matrix a, Matrix b) //矩阵乘法
{
Matrix res;
for (int i = 1; i <= 2 * n; i++)
for (int j = 1; j <= 2 * n; j++)
for (int k = 1; k <= 2 * n; k++)
res.m[i][j] = (res.m[i][j] + a.m[i][k] * b.m[k][j]) % mod;
return res;
}
Matrix fastm(Matrix a, int m) //矩阵快速幂
{
Matrix res;
for (int i = 1; i <= 2 * n; i++) //相当于普通快速幂中的res=1
res.m[i][i] = 1;
while (m)
{
if (m & 1)
res = Multi(res, a);
a = Multi(a, a);
m >>= 1;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
int m;
Matrix num;
cin >> n >> m;
for (int i = 1; i <= n; i++) //构造矩阵B
for (int j = 1; j <= n; j++)
{
cin >> num.m[i][j];
num.m[i][j + n] = num.m[i][j];
}
for (int i = n + 1; i <= 2 * n; i++) //构造矩阵B
num.m[i][i] = 1;
Matrix ans = fastm(num, m);
for (int i = 1; i <= n; i++)
for (int j = n + 1; j <= 2 * n; j++)
{
cout << ans.m[i][j];
if (j != 2 * n)
cout << " ";
else
cout << endl;
}
return 0;
}
问题 C: 关于我转生变成史莱姆这档事
题解: 剪枝+bfs。
第一天取
。
第
天取
我们可以进行提取公因式处理:
如:
化简:
一步步进行,直到左式为0。
而我们的剪枝就是从
的因子进行处理,上述式子可以看成因子的乘积,这样可以减少搜索量。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
vector<int> factor;
bool flag;
int ans = 0x3f3f3f;
void get_factor(int x) //提取因子
{
for (int i = 1; i * i <= x; i++)
{
if (x % i == 0)
factor.push_back(i);
}
}
struct node
{
int num;
int step;
};
void bfs(int x)
{
queue<node> q;
node now, next;
q.push({x, 1});
while (!q.empty())
{
now = q.front();
q.pop();
if (now.num == 0 && now.step != 1)
{
flag = 1;
ans = min(ans, now.step);
break;
}
if (now.step >= ans) //剪枝
break;
for (int i = 2; i <= 9; i++)
{
if (now.num % i == 0)
{
next.num = now.num / i - 1;
next.step = now.step + 1;
q.push(next);
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
int x;
cin >> x;
get_factor(x); //提取因子
for (int i = 0; i < factor.size(); i++) //从x的因子出发
{
int tmp = x / factor[i] - 1;
bfs(tmp);
tmp = x / (x / factor[i]) - 1;
bfs(tmp);
}
if (!flag)
cout << -1 << endl;
else
cout << ans << endl;
return 0;
}