Educational Codeforces Round 64部分題解
Educational Codeforces Round 64部分題解
A
題目大意:給定三角形(高等於低的等腰),正方形,圓,在滿足其高,邊長,半徑最大(保證在上一個圖形的內部)的前提下.
判斷交點個數是否有限,如果有限,輸出.
很明顯當正方形套三角形或者三角形套正方形是交點個數是無限的(因為有一條邊相交)
其他圖形的嵌套交點個數比較好判斷,不多贅述
但是註意坑點:
當按照矩形,園,三角這樣的順序是,三角與圓的一個交點是與圓和正方形的交點重合的,判一下就好了
#include<cstdio> #include<cstring> #include<iostream> #include<vector> #include<algorithm> #include<cctype> using namespace std; int n; inline int read(){ int v = 0,c = 1;char ch = getchar(); while(!isdigit(ch)){ if(ch == '-') c = -1; ch = getchar(); } while(isdigit(ch)){ v = v * 10 - 48 + ch; ch = getchar(); } return v * c; } int a[312312]; bool flag = 1; int ans; int main(){ n = read(); for(int i = 1;i <= n;++i) a[i] = read(); for(int i = 2;i <= n;++i){ if(a[i] == 2 && a[i - 1] == 3) flag = 0; if(a[i] == 3 && a[i - 1] == 2) flag = 0; if(a[i] == 1){ if(a[i - 1] == 2) ans += 3; if(a[i - 1] == 3) ans += 4; } if(a[i] == 2){ if(a[i - 1] == 1) ans += 3; if(i > 2 && a[i - 2] == 3) ans--; } if(a[i] == 3 && a[i - 1] == 1){ ans += 4; } } if(flag) printf("Finite\n%d\n",ans); else printf("Infinite\n"); return 0; }
B
題目大意:給定一個字符串,將其重新排序,使得任意兩個相鄰的字母在字母表上不相鄰(\(a\)與\(z\)不相鄰)
這道題當時卡了幸好unrated
很明顯,我們將奇數字符和偶數字符分開考慮,這樣就保證了奇數之間和偶數之間不會出現上述情況,唯一需要考慮的就是奇數和偶數的交接部分.我們想,如果奇數的最後一個和偶數的第一個相接或者偶數的第一個和奇數的最後一個 相接都不能滿足,那麽就無解(因為我們這樣已經盡可能地去保障了他們不會出現上述情況)
代碼先咕一咕吧
C
題目大意:給定\(n\)個數,求出最多的點對\((i,j)\)使得\(|{a_i-a_j}|>=k\)
求一個\(nlogn\)的做法
很明顯的一個錯誤貪心:
排序之後,對於每個\(a_i\),都找到他後面第一個\(a_j\)使得\(a_j-a_i>=k\),這些操作直接在\(multiset\)(我當時手寫FHQ也是沒誰了)上搞一搞就好了
但是,很明顯是錯的.
看下面一組數據
10 2
1 2 3 4 5 6 7 8 9 10答案為\(5\)
但是用上述方法答案是\(4\)
之後我們發現,我們排完序之後,答案最多為\(\frac{n}{2}\)(下取整),所以至少將前\(\frac{n}{2}\)個與後\(\frac{n}{2}\)個去匹配(很明顯隨著第一個指針右移,第二個也會右移),這個直接維護雙指針,維護當前可用的最小的\(p\)
#include<cstdio>
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N = 2e5 + 3;
int a[N];
int n,k,ans;
int main(){
scanf("%d%d",&n,&k); int p = n / 2 + 1;
for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
sort(a + 1,a + n + 1);
for(int i = 1;i <= n / 2;++i){
for(;p <= n;++p)
if(a[p] - a[i] >= k) break;
if(p > n) break;
ans++;p++;
}
printf("%d\n",ans);
return 0;
}
D
題目大意:給定一個邊權為\(0/1\)的樹,求出滿足從\(x\)點到\(y\)點不會出現經過邊權為\(1\)的邊之後在經過邊權為\(0\)的邊的點對\((x,y)\)的個數
要求一個\(nlogn\)或者更優的算法
這道題聽說可以用\(dsu\)去做,以後再學(flag)
我們考慮\(dp\)
設\(dp_{i,0/1}\)表示以\(i\)點為根時,只經過邊權為\(0/1\)的邊就到達\(i\)的點的個數
想一想,如果我們能夠求出這個東西
那麽
\(ans = \sum_{i = 1}^n dp_{i,0} * dp_{i,1} + dp_{i,0}+dp_{i,1}\)
所以難點在於計算出\(dp\)數組
先考慮在以\(i\)為根的子樹中
很明顯則有
\(dp_{i,w} = \sum_{j\in son_i}(dp_{j,w} + 1)\)
這樣我們就可以求出在\(i\)的子樹中的值了,但是我們想要的是其為根時候的\(dp\)值
那麽我們進行第二遍\(dfs\),想一下
去推出由父親到兒子的貢獻
如果所有點到其父親的路徑是\(dp_{i,w}\)
那麽到他自己也是\(dp_{i,w}\)(因為他們兩個只有一條邊的距離,且這條邊的邊權為\(w\))
在遞歸處理就好了
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#define mk make_pair
#define pii pair<int,int>
#define LL long long
using namespace std;
const int N = 2e5 + 3;
vector <pii> G[N];
LL f[N][2];
int n;
inline void dfs1(int x,int fa){
for(int i = 0;i < G[x].size();++i){
int y = G[x][i].first,z = G[x][i].second;
if(y == fa) continue;
dfs1(y,x);
f[x][z] += f[y][z] + 1;
}
}
inline void dfs2(int x,int fa){
for(int i = 0;i < G[x].size();++i){
int y = G[x][i].first,z = G[x][i].second;
if(y == fa) continue;
f[y][z] = f[x][z];
dfs2(y,x);
}
}
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 - 48 + ch;
ch = getchar();
}
return v * c;
}
int main(){
n = read();
for(int i = 1;i < n;++i){
int x = read(),y = read(),z = read();
G[x].push_back(mk(y,z));
G[y].push_back(mk(x,z));
}
dfs1(1,0);
dfs2(1,0);
LL ans = 0;
for(int i = 1;i <= n;++i)
ans += f[i][0] * f[i][1] + f[i][0] + f[i][1];
cout << ans << endl;
return 0;
}
Educational Codeforces Round 64部分題解