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\n",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\n",ans,1);
else printf("%d/%d\n",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\n",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\n",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\n",((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;
}