首先,感謝中國石油大學的出題組,題的質量很高,很適合剛入門但有一定提高的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;
}