Educational Codeforces Round 64部分题解
Posted wyxdrqc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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部分题解的主要内容,如果未能解决你的问题,请参考以下文章
Educational Codeforces Round 7 A
Educational Codeforces Round 7
Educational Codeforces Round 90
Educational Codeforces Round 33