2019CCPC江西省赛

A-Cotree

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6567

题目大意:有两棵树,在两个树间连一条边,求两两节点之间的距离和

解题思路:两棵树分别称为a,b树,在a,b上分别找一点连接,其实就是找a,b树的重心。
可以先分别求出a,b树内部点距离的和,再找到重心,然后再通过重心求a树上的点到b树上的点的距离和。

推荐题解:https://blog.csdn.net/lalala445566/article/details/97004819

Code:

#include<iostream>
#include<cstring>

using namespace std;
typedef long long ll;
const int N = 5e6+5;
ll h[N],vis[N],cnt;
ll sum[N],sum_num[N];//以该节点为根的树的所有节点到该节点距离的和,,以该节点为根的树的所有子节点 
ll root_1,root_2,sum_root1,sum_root2;

struct Edge{
	ll to,nxt,w;
}eg[N*2]; 

void add(ll u,ll v){//链式前向星建图 
	eg[cnt].w=1;eg[cnt].to=v;eg[cnt].nxt=h[u];h[u]=cnt++;
}

void dfs(ll x){//找到以每个节点为根节点的树的sum值和sum_num值 
	ll num=0;
	ll num_num=0;
	for(ll i=h[x];i!=-1;i=eg[i].nxt){
		ll v = eg[i].to;
		if(!vis[v]){
			vis[v]=1;
			dfs(v);
			num+=sum_num[v]+sum[v]+1;
			num_num+=sum_num[v]+1;
		}
	}
	sum[x]=num;
	sum_num[x]=num_num;
}

ll find_root(ll now,ll &root,ll &sum_root){//now为当前节点,root一直更新为当前最有可能是重心的节点,sum_root所有点到root距离的和 
	ll tot=0;
	tot+=sum[now];//将以所有节点为根的sum值累加 
	ll temp=sum[now];//记忆当前节点的sum值和sum_num值,以便恢复现场 
	ll temp_root=sum_num[now];
	for(ll i=h[now];i!=-1;i=eg[i].nxt){//链式前向星遍历图 
		ll v=eg[i].to;
		if(!vis[v]){
			vis[v]=1;
			//****更新数组,以v为重心是否比以root为重心距离更小 
			sum[now]-=(sum_num[v]+sum[v]+1); 
			sum_num[now]-=(sum_num[v]+1);
			sum[v]+=(sum[now]+sum_num[now]+1);
			sum_num[v]+=(sum_num[now]+1);
			if(sum[v]<sum_root){//如果满足条件,则更新重心及所有节点到重心的距离 
				sum_root=sum[v];
				root=v;
			} 
			tot+=find_root(v,root,sum_root);
			sum[now]=temp;//恢复现场 
			sum_num[now]=temp_root;
		}
	}
	return tot;
}

int main(){
	memset(h,-1,sizeof(h));
	memset(vis,0,sizeof(vis));
	cnt=0;
	ll n,ans=0;
	scanf("%lld",&n);
	ll u,v;
	for(int i=1;i<=n-2;i++){
		scanf("%lld%lld",&u,&v);
		add(u,v);add(v,u);
	}
	root_1=1;
	vis[1]=1;
	dfs(1);//先找一棵树上的sum值和sum_num值 
//	cout<<"*************\n";
	sum_root1=sum[root_1];
	for(int i=1;i<=n;i++){//找另外一棵树 
		if(!vis[i]){
			vis[i]=1;
			root_2=i;
			dfs(i);
			break;
		}
	}
//	cout<<"*************\n";
	sum_root2=sum[root_2];
	memset(vis,0,sizeof(vis));//将访问记录归零 
	vis[root_1]=1;
	ll temp=find_root(root_1,root_1,sum_root1);//temp所存的值是该棵树上以所有节点为根的sum值的总和 ,即该树上所有两点距离和的两倍,不明白可以模拟一下 
	ans+=temp/2;//内部距离和除以二,即为该树内部距离和 
	vis[root_2]=1; 
	temp=find_root(root_2,root_2,sum_root2);//同理 
	ans+=temp/2;
	ans+=((sum[root_1]+sum_num[root_1]+1)*(sum_num[root_2]+1)+sum[root_2]*(sum_num[root_1]+1)); //前半部分是所有a树所有节点到b树重心的距离和乘以b树节点总个数 
																								//后半部分是b树内部距离和乘以a树节点个数 
	cout<<ans<<endl;
	
	return 0;
}

D.Wave

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6570

题目大意:在字符串s中求奇数位置为i,偶数位置为j切i,j不相等的子序列最长为多少

解题思路:dp[i] [j] 存的是奇数位为i偶数为为j的最长长度。对于每个a[i],更新dp[a[i]][j]为偶数时的情况,更新dp[j][a[i]]为奇数时的情况。因为以a[i],j为循环节长度是偶数才能在后面再加一个a[i],以j,a[i]为循环节长度是奇数才能在后面再加一个a[i];

#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) //当时用的else if 所以遍历样例到2时,dp[1][2]就连不上了 
				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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6572

题目大意:在字符串s中随机抽四个字母,求四个字母组合是avin的概率

解题思路:遍历s求字符串中a,v,i,n的个数,求出他们的乘积,再求出字符串长度的四次方,然后两者约分按格式输出即可

#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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6573

题目大意:有从东到西行驶的车和从南到北行驶的车要使不发生车祸,求从南到北行驶的车最短停留时间

解题思路:如果要等,所有从南到北行驶的车都要等待相同的时间才能走,暴力枚举1到1000,符合条件break输出即可

#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int a[1050],b[1050],c[1050];

bool check(int x){
	for(int i=0;i<m;i++) c[i]=b[i];
	for(int i=0;i<m;i++) c[i]+=x;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			if(a[i]==c[j]) return false;
		}
	}
	return true;
}

int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int j=0;j<m;j++) cin>>b[j];
	sort(a,a+n);
	sort(b,b+m);
	int ans;
	for(int i=0;i<=1000;i++){
		if(check(i)){
			ans=i;
			break;
		}
	}
	cout<<ans<<endl;
	
	return 0;
}

H - Rng

题目链接:u.edu.cn/showproblem.php?pid=6574

题目大意,在[1,c]中,找一个整数r,再从[1,r]中找一个整数l,变为一个新范围[l,r],这样重复两次问两次范围相交的概率

解题思路:打表找规律,发现规律为(n+1)/(2*n),因为数比较大,需要用到逆元

#include<iostream>

using namespace std;
typedef long long ll;
const ll mod = 1e9+7;

ll ksm(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1) res=(res*a)%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}

int main(){
	ll n;
	cin>>n;
	cout<<(n+1)%mod*ksm(2*n,mod-2)%mod<<'\n';
	
	
	return 0;
}

I - Budget

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6575

题目大意:将公司所有账目从三位小数保留成两位,问更新后差值是多少

解题思路:将差额求出来输出即可

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int n;
char p[20];

int main(){
	scanf("%d",&n);
	double ans=0;
	while(n--){
		scanf("%s",p);
		int len=strlen(p);
		int temp=p[len-1]-'0';
		if(temp<=4) {
			double t=temp*1.0/1000;
			ans-=t;
		}
		else {
			double t=temp*1.0/1000;
			ans+=(0.01-t);
		}
	}
	double pp;
//	ans=4.004;
	if(ans<0) pp=-ans;
	else pp=ans;
	if(pp>=0&&pp<=1.0) printf("%.3lf\n",ans);
	else if(pp>1.0){
		int num=(int)(pp*1000);
		int cnt=num%10;
		if(cnt<=4) printf("%.2lf\n",ans);
		else{
			if(ans<0) printf("%.2lf\n",ans-0.01);
			else printf("%.2lf\n",ans+0.01);
		}
	}
	
	return 0;
}

J - Worker

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6576

题目大意:有n个工厂m个工人,每个工厂产出不同,问怎样分配工人使每个工厂产出相同

解题思路:找到工厂产出的最小公倍数,然后求出满足相同产出的最少工人数,然后求出总工人对最少工人数的倍数,然后求出每个工厂所需最少工人数乘以倍数即为每个工厂分配工人数,输出即可

#include<iostream>
#include<algorithm>

using namespace std;
typedef long long ll;
const int N = 1005;
ll a[N],b[N];
ll n,m;

int main(){
	cin>>n>>m;
	ll ans=1;
	for(ll i=0;i<n;i++){
		cin>>a[i];
		ans=ans*a[i]/__gcd(ans,a[i]);
	}
	ll sum=0;
	for(ll i=0;i<n;i++){
		b[i]=ans/a[i];
		sum+=b[i];
	}
	if(m%sum==0) {
		puts("Yes");
		ll t=m/sum;
		for(ll i=0;i<n;i++){
			if(i) printf(" %lld",b[i]*t);
			else printf("%lld",b[i]*t);
		}
		puts("");
	}
	else puts("No");
	
	return 0;
}

K - Class

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6577

题目大意:x,y分别是a,b的和,差,求a*b

解题思路:求出a,b;相乘即可

#include<iostream>

using namespace std;

int main(){
	int x,y;
	cin>>x>>y;
	int a,b;
	a=(x+y)/2;
	b=x-a;
	cout<<a*b<<endl;
	
	return 0;
}
posted @ 2019-11-11 22:22  voids5  阅读(113)  评论(0编辑  收藏  举报