CDZSC_2022寒假個人訓練賽21級(10)題解
- 簡單
- D 列舉、dp、數學
- G 素數
- 中等
- A
- B dfs
- E 模擬、數學
- F 貪心
- 困難
- C 構造
- H 整數的唯一分解定理+約數和定理+遞迴二分求等比數列+快速冪
A Visiting a Friend CodeForces - 902A
題意
在數軸上你從0點出發,要到達m點,只能通過傳送點移動,傳送點給你一對a,b,a代表傳送點的位置,b代表最遠可以傳送的長度。問你能不能到m點。
題解
首先因為只能通過傳送點移動,0點必須有傳送點,然後為了能到達m點,0點到m點之間必須被傳送段完全覆蓋,至於實現,只要維護一個目前能達到的最遠距離就好。
AC程式碼
#include<iostream> using namespace std; int main(){ int n,m,a,b,r=0,flag=0; scanf("%d%d",&n,&m); for(int i=0;i<n;i++){ scanf("%d%d",&a,&b); if(a==0)flag=1; if(a<=r)r=r>b?r:b; } if(r<m)flag=0; printf("%s\n",flag?"YES":"NO"); return 0; }
B Coloring a Tree CodeForces - 902B
題意
給你顆樹,每一次可以選擇一個子樹全染成一種顏色,問你要染成要求的顏色最少要多少步。
題解
因為染子樹,所以上面染完必然會覆蓋下面的,從根到葉dfs遍歷,父節點和子節點要求顏色不同的最終答案就加1,根節點要染一次所以額外還要加一次,我下面的寫法是用了一個假父節點0來方便的處理這個問題的。
AC程式碼
#include<iostream> #include<algorithm> #include<vector> using namespace std; vector<int> e[100005]; int v[100005]; int ans; void dfs(int x, int f) { for (auto i : e[x]) { if (i != f) { if (v[i] != v[x])ans++; dfs(i, x); } } } int main() { int n, x; scanf("%d", &n); for (int i = 2; i <= n; i++) { scanf("%d", &x); e[i].emplace_back(x); e[x].emplace_back(i); } for (int i = 1; i <= n; i++) { scanf("%d", &x); v[i] = x; } ans++; dfs(1, 0); printf("%d\n", ans); return 0; }
C Hashing Trees CodeForces - 902C
題意
給出一顆n層的樹。以及從第0層開始給出每層的節點個數。求是否存在同構,若存在輸出“ambiguous”和 2 個可以同構的樹,如果不可以輸出“perfect”。
題解
很容易知道,只要相鄰的兩層節點數都大於1就一定有同構。
AC程式碼
#include<iostream> using namespace std; int s[100005]; int p[200005]; int main(){ int n; scanf("%d",&n); int flag=0; for(int i=0;i<=n;i++){ scanf("%d",s+i); if(i&&s[i]>1&&s[i-1]>1) { flag=i; } } if(!flag){ printf("perfect\n"); scanf(" "); return 0; } int f=0; for(int i=0;i<=n;i++){ int x=f; while(f-x<s[i])p[f++]=x; if(i&&s[i]>1&&s[i-1]>1) { flag=f-1; } } printf("ambiguous\n"); for(int i=0;i<f;i++) printf("%d ",p[i]); printf("\n"); for(int i=0;i<f;i++){ if(i==flag)p[i]--; printf("%d ",p[i]); } printf("\n"); scanf(" "); return 0; }
D Hungry Student Problem CodeForces - 903A
題意
\(T\)次詢問,每次問有沒有兩個非負整數\(x,y\)使得\(3*x + 7*y = n\)。
題解
資料很小怎麼做都行。
- 方法一:兩個for暴力x,y
- 方法二:dp,其實和揹包差不多
- 方法三:數學方法,其實這種互質的組成數的問題,只要大於一定的數字就一定可以組成所有數,比如這個的3和7,實際上大於11一定有解。
AC程式碼
//方法二
#include<iostream>
using namespace std;
int dp[200];
int main() {
int n, x;
dp[0] = 1;
for (int i = 3; i <= 100; i++)
dp[i] = dp[i - 3] ? 1 : dp[i];
for (int i = 7; i <= 100; i++)
dp[i] = dp[i - 7] ? 1 : dp[i];
scanf("%d", &n);
while (n--) {
scanf("%d", &x);
printf("%s\n", dp[x] ? "YES" : "NO");
}
}
//方法三
#include<iostream>
using namespace std;
int ans[105]={1,1,1,0,1,1,0,0,1,0,0,1};
int main() {
int n,x;
scanf("%d", &n);
while (n--) {
scanf("%d", &x);
printf("%s\n",!ans[x] ? "YES" : "NO");
}
}
E The Modcrab CodeForces - 903B
題意
一個人在打怪,他每回合可以選擇喝藥加血或者打怪,然後怪物會攻擊他一次,讓你輸出一個他可以打死怪物的方案(任意一個即可,特殊判定),但是注意他的血量可以無上限。第一行輸入他的當前血量h1,攻擊力a1,藥每次回覆c的血量,第二行輸入怪物的血量h2,怪物的攻擊力a2,。
題解
由於你回血量一定高於攻擊力,所以,一直打怪,如果下一次要被殺死了,再回血就好,當然先把血回到可以殺死怪物的狀態也行,那樣可能還會更快點,但反正資料範圍很小,隨便做。
AC程式碼
#include<iostream>
using namespace std;
int p[10500];
int main() {
int h1, h2, a1, a2, c,k=0;
scanf("%d%d%d%d%d", &h1,&a1,&c,&h2,&a2);
while (h2 > 0) {
if (h2 - a1 <= 0) {
p[k] = 1;
h2 -= a1;
}
else if (h1 - a2 > 0) {
p[k] = 1;
h2 -= a1;
}
else h1 += c;
k++;
h1 -= a2;
}
printf("%d\n", k);
for (int i = 0; i < k; i++) {
printf("%s\n", p[i] ? "STRIKE" : "HEAL");
}
}
#include<iostream>
using namespace std;
int main() {
int h1, h2, a1, a2, c1;
scanf("%d%d%d", &h1, &a1, &c1);
scanf("%d%d", &h2, &a2);
int n = (h2 + a1 - 1) / a1;//需要攻擊幾次
int h = (n-1) * a2+1; //需要多少血
int m = (h - h1 + (c1 - a2 - 1)) / (c1 - a2);//需要回幾次血
if (m < 0)m = 0;
printf("%d\n", n + m);
for (int i = 0; i < m; i++)
printf("HEAL\n");
for (int i = 0; i < n; i++)
printf("STRIKE\n");
}
F Boxes Packing CodeForces - 903C
題意
給你n個盒子,把小盒子放進大的盒子裡,這樣只能看到大盒子,輸出最小能夠有看到的盒子數。
題解
貪心,可以看成小的盒子替換大的盒子,因為如果大盒子套個小的,那那個大盒子能繼續套的只能是比那個小盒子還小的盒子了。排序後找第一個比自己小的巢狀。
AC程式碼
#include<iostream>
#include<algorithm>
using namespace std;
int s[5005];
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=0;i<n;i++)
scanf("%d",s+i);
sort(s,s+n);
int j=n-1;
for(int i=j-1;i>=0;i--)
if(s[i]<s[j])
j--;
printf("%d\n",j+1);
}
scanf(" ");
return 0;
}
G 結果填空:有趣的數字 計蒜客 - 43115
題解
直接輸出結果就好,資料範圍很小,又是直接輸入結果的題,所以實際上連素數篩都不用,暴力,哪怕是最差的\(O(n^2)\)複雜度的暴力都能過,就是要掛機跑一會。我下面是一個優化的暴力,複雜度為\(O(n\sqrt{n})\),篩法我就不寫了,大家應該都會吧,尤拉篩和埃式篩都行。
(\(O(n^2)\)複雜度的暴力我電腦跑十幾秒也出了)
AC程式碼
#include<iostream>
using namespace std;
int main() {
// int ans = 0;
// for (int i = 2; i <= 100000; i++) {
// int flag = 1;
// for (int j = 2; j*j <= i; j++) {
// if (i%j == 0) {
// flag = 0;
// }
// }
// if (flag) {
// int x = i;
// while (x) {
// if (x % 10 == 5) {
// ans++;
// break;
// }
// x /= 10;
// }
// }
// }
// printf("%d\n", ans);
printf("3282");
}
H Sumdiv OpenJ_Bailian - 1845
題意
求\(A^B\)的約數和對9901取餘的結果。
題解
使用約數和定理求值,具體實現用遞迴二分求,求值的時候有大指數,使用快速冪解決。
AC程式碼
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll p[100005], c[100005];
ll pow(ll x, ll y,ll mod) {
ll ans = 1%mod;
while (y) {
if (y & 1)ans = (ans*x) % mod;
x = (x*x) % mod;
y >>= 1;
}
return ans;
}
int k[105];
ll sum(ll p, ll c,ll mod) {
if (c == 0)return 1;
if (c == 1)return (1 + p) % mod;
if (c % 2)return ((1 + pow(p, (c + 1) / 2, mod))*sum(p, (c - 1) / 2,mod))%mod;
else return ((1 + pow(p, c / 2,mod))*sum(p, c / 2 - 1,mod) + pow(p, c,mod)) % mod;
}
int main() {
ll a, b;
ll mod = 9901;
scanf("%lld%lld", &a, &b);
int m=0;
for (int i = 2; i*i <= a; i++) {
if (a%i == 0) {
p[m] = i;
c[m] = 0;
while (a%i == 0) {
a /= i;
c[m]++;
}
m++;
}
} if (a != 1) {
p[m] = a;
c[m++] = 1;
}
ll ans = 1;
for (int i = 0; i < m; i++) {
ans = (ans*sum(p[i], c[i] * b,mod)) % mod;
}
printf("%lld\n", ans);
}