hpu18級第一次訓練賽

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章