A - Cotree
题目链接:https://vjudge.net/contest/341543#problem/A
要使连接之后的那个值最小就要找两棵树的重心.
先来补一下树的重心:
定义:
找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡
性质:
(一)
树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
(二)
把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
(三)
把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
所以连接重心后求得的答案最小.
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1e5+100;
struct node
{
int to,next;
}ed[maxn*2];
int head[maxn],cnt=0;
void add(int u,int v)
{
ed[cnt].to=v;
ed[cnt].next=head[u];
head[u]=cnt++;
}
bool vis[maxn];
int dfs(int x)//遍历两棵树,求两棵树的节点数
{
int res=0;
for(int i=head[x];i!=-1;i=ed[i].next)
{
int dx=ed[i].to;
if(!vis[dx])
{
vis[dx]=1;
res+=dfs(dx);
}
}
return res+1;
}
int n,nn,mm,mins,n1,n2;
int dfs1(int u,int pre,int n)//求两棵树的重心,pre可以当做u的父节点
{
int sum=0,mx=0;
for(int i=head[u];i!=-1;i=ed[i].next)
{
int v=ed[i].to;
if(v!=pre)
{
int t=dfs1(v,u,n);//这个点子树的节点数
sum+=t;//子树节点数之和
mx=max(t,mx);
}
}
int x=sum;
sum=max(n-sum-1,sum);//节点来的那个方向也是一颗子树,所以求节点两边的最大子树的节点数
if(sum<mins) //最小的sum的那个点就是重心
{
mm=u;
mins=sum;
}
return x+1;//返回这个节点所有子树节点和,包括自身
}
long long ans=0;//保存结果
int dfs2(int u,int pre)
{
int res=1;
for(int i=head[u];i!=-1;i=ed[i].next)
{
int v=ed[i].to;
if(v!=pre)
{
int t=dfs2(v,u);
ans+=(long long)(t)*(n-t);//每条边两端节点数相乘就是这条边被用的次数,注意爆int
res+=t;//子树的节点和
}
}
return res;
}
int main()
{
memset(head,-1,sizeof head);
int x,y;
scanf("%d",&n);
for(int i=0;i<n-2;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
memset(vis,0,sizeof vis);
int s1,s2;
for(int i=1;i<n;i++)
if(!vis[i])
{
if(i==1)
{
n1=dfs(i);
s1=i;
}
else
{
s2=i;
n2=dfs(i);
}
}
mins=0x3f3f3f3f;
dfs1(s1,-1,n1);//求两棵树的重心
int g1=mm;
mins=0x3f3f3f3f;
dfs1(s2,-1,n2);
int g2=mm;
add(g1,g2);//把重心连接
add(g2,g1);
dfs2(1,-1);
printf("%lld
",ans);
return 0;
}
D - Wave
题目链接:https://vjudge.net/contest/341543#problem/D
题意:定义 波为 至少有两个元素的字符串的奇数位和偶数位分别是相同的,而且奇偶位不相同,求给定序列的子序列是波的最大长度
思路:开一个二维dp数组,dp [ i ] [ j ] 表示奇数位是i 偶数位是j的波的长度,全部初始化为0;
遍历一遍字符串,假设当前字符是a,这个字符可以组成两种波,分别是dp[ a ] [ x ]和dp [ x ][ a ](x为非a小于c的任意数字),当dp[ a ][ x ]为偶数时,a可以填到这个波的奇数位;同理,当dp[ x ][ a ]为奇数时a可填到偶数位,其他情况不能添加就不用更新dp值了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int dp[200][200],s[100010];
int main()
{
int n,c;
scanf("%d %d",&n,&c);
memset(dp,0,sizeof dp);
for(int i=0;i<n;i++)
{
scanf("%d",&s[i]);
for(int j=1;j<=c;j++)
{
if(j==s[i]) continue;
if(dp[s[i]][j]%2==0)
dp[s[i]][j]++;
if(dp[j][s[i]]&1)
dp[j][s[i]]++;
}
}
int ans=0;
for(int i=1;i<=c;i++)
for(int j=1;j<=c;j++)
{
if(j==i) continue;
ans=max(ans,dp[i][j]);
}
cout<<ans<<endl;
return 0;
}
F - String
题目链接:https://vjudge.net/contest/341543#problem/F
ans=num[‘a’]*num[‘v’]*num[‘i’]*num[‘n’]/pow(n,4);
#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
map<char,int>ma;
int main(){
int n;
string s;
cin>>n>>s;
for(int i=0;i<n;i++){
ma[s[i]]++;
}
int a=ma['a'],b=ma['v'],c=ma['i'],d=ma['n'];
int ans,res;
ans=a*b*c*d;
res=n*n*n*n;
int temp=__gcd(ans,res);
if(ans==0)
printf("%d/%d
",ans,1);
else printf("%d/%d
",ans/temp,res/temp); //注意要约分
return 0;
}
G - Traffic
题目链接:https://vjudge.net/contest/341543#problem/G
这道题好坑,题意看错好久,当有车通过时间相同时,南北方向的要让路,而且是所有南北的车都要等待!!!看懂题意后暴力即可
#include<bits/stdc++.h>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
int a[2000],b[2000],c[5000];
memset(c,0,sizeof c);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&b[i]);
}
sort(a,a+n);
sort(b,b+m);
int x=b[1],i=1;
while(i<=m)
{
int j=upper_bound(a,a+n,b[i])-a;
if((a[j-1]==b[i]&&j>1)||(j==n&&b[i]==a[j]))
{
for(int k=1;k<=m;k++)
b[k]++;
i=1;
}
else
i++;
}
printf("%d
",b[1]-x);
}
return 0;
}```
I - Budget
题目链接:https://vjudge.net/contest/341543#problem/I
当做字符串输入,计算最后一位即可
```cpp
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
int main()
{
char s[50];
int n;
while(~scanf("%d",&n))
{
double res=0;
for(int k=0;k<n;k++)
{
scanf("%s",s);
int len=strlen(s);
for(int i=0;i<len;i++)
{
if(s[i]=='.')
{
if(s[i+3]>='5')
res+=(0.01-(s[i+3]-'0')*1.0/1000);
else
res+=(0-(s[i+3]-'0')*1.0/1000);
break;
}
}
}
printf("%.3lf
",res);
}
return 0;
}
J - Worker
题目链接:https://vjudge.net/contest/341543#problem/J
计算n个数的最小公倍数,这个数就是所有仓库人数相等的最小数,计算所有仓库需要的人数,如果能被m整除,就输出yes,否则输出no
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int main()
{
int n,s[2000],num[20];
ll m;
while(~scanf("%d %lld",&n,&m))
{
memset(num,0,sizeof num);
for(int i=0;i<n;i++)
{
scanf("%d",&s[i]);
num[s[i]]++;
}
int ans=s[0];
int res=s[0];
for(int i=1;i<=10;i++)
if(num[i])
{
res=__gcd(ans,i);
ans=ans*i;
ans/=res;
}
res=0;
for(int i=1;i<=10;i++)
if(num[i])
res+=(ans/i*num[i]);
if(m%res==0)
{
puts("Yes");
for(int i=0;i<n;i++)
if(i==n-1)
printf("%lld
",((m/res)*(ans/s[i])));
else
printf("%lld ",((m/res)*(ans/s[i])));
// puts("");
}
else
{
puts("No");
}
}
return 0;
}
K - Class
题目链接:https://vjudge.net/contest/341543#problem/K
#include<iostream>
using namespace std;
int main()
{
int x,y,a,b;
cin>>x>>y;
a=(x+y)/2;
b=(x-y)/2;
cout<<a*b<<endl;//不加换行就PE
return 0;
}